181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada/*
281d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada * Copyright (C) 2012 The Android Open Source Project
381d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada *
1081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada * Unless required by applicable law or agreed to in writing, software
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
1581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada */
1681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
1781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanadapackage com.android.inputmethod.latin.makedict;
1881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
197d1ae52ded0deca6b2674df0273ec852ad36319fYuichiro Hanadaimport com.android.inputmethod.annotations.UsedForTesting;
2081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanadaimport com.android.inputmethod.latin.Constants;
2181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
223ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagiimport java.util.Date;
233ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagiimport java.util.HashMap;
240e40cd0c40f2c731f91ccd0561e251262e5a2614Yuichiro Hanada
2581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada/**
2681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada * Dictionary File Format Specification.
2781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada */
2881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanadapublic final class FormatSpec {
2981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
3081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    /*
317ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * File header layout is as follows:
327ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *
337ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * v |
347ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * e | MAGIC_NUMBER + version of the file format, 2 bytes.
357ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * r |
367ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * sion
377ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *
387ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * o |
399514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada     * p | not used                                3 bits
409514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada     * t | each unigram and bigram entry has a time stamp?
419514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada     * i |                                         1 bit, 1 = yes, 0 = no : CONTAINS_TIMESTAMP_FLAG
427b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * o |
437b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * nflags
447ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *
457ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * h |
467ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * e | size of the file header, 4bytes
477ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * a |   including the size of the magic number, the option flags and the header size
487ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * d |
497ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * ersize
507ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *
517ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *   | attributes list
527ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *
537ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * attributes list is:
547ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * <key>   = | string of characters at the char format described below, with the terminator used
557ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *           | to signal the end of the string.
567ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * <value> = | string of characters at the char format described below, with the terminator used
577ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *           | to signal the end of the string.
587ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     * if the size of already read < headersize, goto key.
597ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     *
607ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada     */
617ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada
627ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada    /*
63af30cbf0ee8370763edf22822ea34a282e882084Jean Chalard     * Node array (FusionDictionary.PtNodeArray) layout is as follows:
6481d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
65576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * n |
66576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * o | the number of PtNodes, 1 or 2 bytes.
67576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * d | 1 byte = bbbbbbbb match
68576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * e |   case 1xxxxxxx => xxxxxxx << 8 + next byte
69576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * c |   otherwise => bbbbbbbb
70576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * o |
71576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * unt
7281d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
73576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * n |
74576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * o | sequence of PtNodes,
75576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * d | the layout of each PtNode is described below.
76576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * e |
77576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * s
7881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
79061d225fb1d110695b396a470d9ae6a9a3331003Yuichiro Hanada     * f |
807b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * o | forward link address, 3byte
817b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * r | 1 byte = bbbbbbbb match
827b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * w |   case 1xxxxxxx => -((xxxxxxx << 16) + (next byte << 8) + next byte)
837b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * a |   otherwise => (xxxxxxx << 16) + (next byte << 8) + next byte
847b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * r |
857b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * dlinkaddress
8681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     */
8781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
88576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    /* Node (FusionDictionary.PtNode) layout is as follows:
897b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     *   | is moved ?             2 bits, 11 = no          : FLAG_IS_NOT_MOVED
907b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     *   |                          This must be the same as FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
917b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     *   |                                01 = yes         : FLAG_IS_MOVED
927b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * f |                      the new address is stored in the same place as the parent address
937b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * l | is deleted?                    10 = yes         : FLAG_IS_DELETED
947b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * a | has several chars ?         1 bit, 1 = yes, 0 = no   : FLAG_HAS_MULTIPLE_CHARS
957b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * g | has a terminal ?            1 bit, 1 = yes, 0 = no   : FLAG_IS_TERMINAL
967b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * s | has shortcut targets ?      1 bit, 1 = yes, 0 = no   : FLAG_HAS_SHORTCUT_TARGETS
9781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *   | has bigrams ?               1 bit, 1 = yes, 0 = no   : FLAG_HAS_BIGRAMS
9881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *   | is not a word ?             1 bit, 1 = yes, 0 = no   : FLAG_IS_NOT_A_WORD
9981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *   | is blacklisted ?            1 bit, 1 = yes, 0 = no   : FLAG_IS_BLACKLISTED
10081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
10181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * p |
1027b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * a | parent address, 3byte
1037b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * r | 1 byte = bbbbbbbb match
1047b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * e |   case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
1057b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * n |   otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte
1067b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * t | This address is relative to the head of the PtNode.
1077b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * a | If the node doesn't have a parent, this field is set to 0.
108663f5375d1bad660a1fdfb43a3fb8dd1c3f64008Yuichiro Hanada     * d |
1097b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * dress
11081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
11181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * c | IF FLAG_HAS_MULTIPLE_CHARS
112576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * h |   char, char, char, char    n * (1 or 3 bytes) : use PtNodeInfo for i/o helpers
11381d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * a |   end                       1 byte, = 0
11481d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * r | ELSE
11581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * s |   char                      1 or 3 bytes
11681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *   | END
11781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
11881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * f |
11981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * r | IF FLAG_IS_TERMINAL
12081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * e |   frequency                 1 byte
12181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * q |
12281d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
1237b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * c |
1247b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * h | children address, 3 bytes
1257b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * i | 1 byte = bbbbbbbb match
1267b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * l |   case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
1277b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * d |   otherwise => (bbbbbbbb<<16) + (next byte << 8) + next byte
1287b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * r | if this node doesn't have children, this field is set to 0.
1297b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * e |   (see BinaryDictEncoderUtils#writeVariableSignedAddress)
1307b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * n | This address is relative to the position of this field.
1317b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * a |
1327b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard     * ddress
13381d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
13481d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *   | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS
13581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *   | shortcut string list
13681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *   | IF FLAG_IS_TERMINAL && FLAG_HAS_BIGRAMS
13781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *   | bigrams address list
13881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
13981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * Char format is:
14081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * 1 byte = bbbbbbbb match
14181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
14281d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
14381d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *       unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
14481d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *       00011111 would be outside unicode.
14581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * else: iso-latin-1 code
14681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * This allows for the whole unicode range to be encoded, including chars outside of
14781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
14881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * characters which should never happen anyway (and still work, but take 3 bytes).
14981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
15081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * bigram address list is:
151576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no     : FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT
152576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           | addressSign = 1 bit,                 : FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE
15381d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *           |                      1 = must take -address, 0 = must take +address
154576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           |                         xx : mask with MASK_BIGRAM_ATTR_ADDRESS_TYPE
155576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           | addressFormat = 2 bits, 00 = unused  : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE
156576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           |                         01 = 1 byte  : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE
157576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           |                         10 = 2 bytes : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES
158576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           |                         11 = 3 bytes : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES
159576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           | 4 bits : frequency         : mask with FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY
160576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * <address> | IF (01 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE == addressFormat)
16181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *           |   read 1 byte, add top 4 bits
162576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           | ELSIF (10 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES == addressFormat)
16381d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *           |   read 2 bytes, add top 4 bits
164576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           | ELSE // 11 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES == addressFormat
16581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *           |   read 3 bytes, add top 4 bits
16681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *           | END
167576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *           | if (FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) then address = -address
168576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * if (FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) goto bigram_and_shortcut_address_list_is
16981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *
17081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * shortcut string list is:
171576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * <byte size> = PTNODE_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes.
172576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * <flags>     = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT
17381d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *               | reserved = 3 bits, must be 0
174576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     *               | 4 bits : frequency : mask with FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY
17581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     * <shortcut>  = | string of characters at the char format described above, with the terminator
17681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     *               | used to signal the end of the string.
177576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada     * if (FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT goto flags
17881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada     */
17981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
1807ec9db2c34ee6bec2cbff6cf05cee9bf3c2f7122Yuichiro Hanada    public static final int MAGIC_NUMBER = 0x9BC13AFE;
18181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int NOT_A_VERSION_NUMBER = -1;
18282d9deaaf252cd20f8918adbc7a4b9b8f2647c38Yuichiro Hanada    static final int FIRST_VERSION_WITH_DYNAMIC_UPDATE = 3;
183a141d8ef7dcf8f942eb7bd4ca006f63da1744319Yuichiro Hanada    static final int FIRST_VERSION_WITH_TERMINAL_ID = 4;
1842fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa
1852fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // These MUST have the same values as the relevant constants in format_utils.h.
1862fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // From version 4 on, we use version * 100 + revision as a version number. That allows
1872fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // us to change the format during development while having testing devices remove
1882fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // older files with each upgrade, while still having a readable versioning scheme.
189455dc84cf2c6526329b535f30000ea45b7d4d4d7Keisuke Kuroyanagi    // When we bump up the dictionary format version, we should update
190455dc84cf2c6526329b535f30000ea45b7d4d4d7Keisuke Kuroyanagi    // ExpandableDictionary.needsToMigrateDictionary() and
191455dc84cf2c6526329b535f30000ea45b7d4d4d7Keisuke Kuroyanagi    // ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType().
1922fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public static final int VERSION2 = 2;
193455dc84cf2c6526329b535f30000ea45b7d4d4d7Keisuke Kuroyanagi    // Dictionary version used for testing.
194455dc84cf2c6526329b535f30000ea45b7d4d4d7Keisuke Kuroyanagi    public static final int VERSION4_ONLY_FOR_TESTING = 399;
195a37f374ad140f14e5e8ecaef9e1dbee3b1d7b84cKeisuke Kuroyanagi    public static final int VERSION401 = 401;
196a37f374ad140f14e5e8ecaef9e1dbee3b1d7b84cKeisuke Kuroyanagi    public static final int VERSION4 = 402;
197a37f374ad140f14e5e8ecaef9e1dbee3b1d7b84cKeisuke Kuroyanagi    public static final int VERSION4_DEV = 403;
1982fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    static final int MINIMUM_SUPPORTED_VERSION = VERSION2;
19904536f14b02566ead3a95fc7d80d47e8d99936edKeisuke Kuroyanagi    static final int MAXIMUM_SUPPORTED_VERSION = VERSION4_DEV;
20081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
20181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    // TODO: Make this value adaptative to content data, store it in the header, and
20281d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    // use it in the reading code.
203ffcbbaf12788a9fc9398607a548e552d7d2bf05eSatoshi Kataoka    static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
20481d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
20581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int PARENT_ADDRESS_SIZE = 3;
206061d225fb1d110695b396a470d9ae6a9a3331003Yuichiro Hanada    static final int FORWARD_LINK_ADDRESS_SIZE = 3;
20781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
2084ad4ff618f5102148d73e3c04d809942bcf16f86Yuichiro Hanada    // These flags are used only in the static dictionary.
209576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int MASK_CHILDREN_ADDRESS_TYPE = 0xC0;
210576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS = 0x00;
211576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE = 0x40;
212576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES = 0x80;
213576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = 0xC0;
21481d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
21581d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int FLAG_HAS_MULTIPLE_CHARS = 0x20;
21681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
21781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int FLAG_IS_TERMINAL = 0x10;
21881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int FLAG_HAS_SHORTCUT_TARGETS = 0x08;
21981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int FLAG_HAS_BIGRAMS = 0x04;
22081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int FLAG_IS_NOT_A_WORD = 0x02;
22181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int FLAG_IS_BLACKLISTED = 0x01;
2224ad4ff618f5102148d73e3c04d809942bcf16f86Yuichiro Hanada
2234ad4ff618f5102148d73e3c04d809942bcf16f86Yuichiro Hanada    // These flags are used only in the dynamic dictionary.
224c3a98ca306d5d6a3dfce3585b73f7431dbf90bfcYuichiro Hanada    static final int MASK_MOVE_AND_DELETE_FLAG = 0xC0;
2254ad4ff618f5102148d73e3c04d809942bcf16f86Yuichiro Hanada    static final int FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE = 0x40;
2264ad4ff618f5102148d73e3c04d809942bcf16f86Yuichiro Hanada    static final int FLAG_IS_MOVED = 0x00 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
2274ad4ff618f5102148d73e3c04d809942bcf16f86Yuichiro Hanada    static final int FLAG_IS_NOT_MOVED = 0x80 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
228a853356b82e2dc74962243e3143c0ff7a33f3c20Yuichiro Hanada    static final int FLAG_IS_DELETED = 0x80;
22981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
230576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT = 0x80;
231576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE = 0x40;
232576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int MASK_BIGRAM_ATTR_ADDRESS_TYPE = 0x30;
233576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE = 0x10;
234576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES = 0x20;
235576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES = 0x30;
236576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY = 0x0F;
23781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
238576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int PTNODE_CHARACTERS_TERMINATOR = 0x1F;
23981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
240576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int PTNODE_TERMINATOR_SIZE = 1;
241576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int PTNODE_FLAGS_SIZE = 1;
242576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int PTNODE_FREQUENCY_SIZE = 1;
243a141d8ef7dcf8f942eb7bd4ca006f63da1744319Yuichiro Hanada    static final int PTNODE_TERMINAL_ID_SIZE = 4;
244576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int PTNODE_MAX_ADDRESS_SIZE = 3;
245576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int PTNODE_ATTRIBUTE_FLAGS_SIZE = 1;
246576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
247576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int PTNODE_SHORTCUT_LIST_SIZE_SIZE = 2;
24881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
2492fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // These values are used only by version 4 or later. They MUST match the definitions in
2502fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // ver4_dict_constants.cpp.
251a141d8ef7dcf8f942eb7bd4ca006f63da1744319Yuichiro Hanada    static final String TRIE_FILE_EXTENSION = ".trie";
2522fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    public static final String HEADER_FILE_EXTENSION = ".header";
253a141d8ef7dcf8f942eb7bd4ca006f63da1744319Yuichiro Hanada    static final String FREQ_FILE_EXTENSION = ".freq";
25422c5c450fecb856100059f4e5b34b847fb0acfa7Yuichiro Hanada    // tat = Terminal Address Table
25522c5c450fecb856100059f4e5b34b847fb0acfa7Yuichiro Hanada    static final String TERMINAL_ADDRESS_TABLE_FILE_EXTENSION = ".tat";
256fd46e87da2e91583e0b0ab0dd7ffa5e329012861Yuichiro Hanada    static final String BIGRAM_FILE_EXTENSION = ".bigram";
25773b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada    static final String SHORTCUT_FILE_EXTENSION = ".shortcut";
2583dd77a6d6696bb426b200b27adeb8be7e887a667Yuichiro Hanada    static final String LOOKUP_TABLE_FILE_SUFFIX = "_lookup";
2593dd77a6d6696bb426b200b27adeb8be7e887a667Yuichiro Hanada    static final String CONTENT_TABLE_FILE_SUFFIX = "_index";
26026bd46095a05843e7574dfcf7db53406f215525dKeisuke Kuroyanagi    static final int FLAGS_IN_FREQ_FILE_SIZE = 1;
261a141d8ef7dcf8f942eb7bd4ca006f63da1744319Yuichiro Hanada    static final int FREQUENCY_AND_FLAGS_SIZE = 2;
26222c5c450fecb856100059f4e5b34b847fb0acfa7Yuichiro Hanada    static final int TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE = 3;
263c32962b8f1f9b7255fef84486b53cfc874835bbdYuichiro Hanada    static final int UNIGRAM_TIMESTAMP_SIZE = 4;
26426bd46095a05843e7574dfcf7db53406f215525dKeisuke Kuroyanagi    static final int UNIGRAM_COUNTER_SIZE = 1;
26526bd46095a05843e7574dfcf7db53406f215525dKeisuke Kuroyanagi    static final int UNIGRAM_LEVEL_SIZE = 1;
26673b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada
26773b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada    // With the English main dictionary as of October 2013, the size of bigram address table is
2682fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // is 345KB with the block size being 16.
2692fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // This is 54% of that of full address table.
2702fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 16;
27126bd46095a05843e7574dfcf7db53406f215525dKeisuke Kuroyanagi    static final int BIGRAM_CONTENT_COUNT = 1;
2723dd77a6d6696bb426b200b27adeb8be7e887a667Yuichiro Hanada    static final int BIGRAM_FREQ_CONTENT_INDEX = 0;
2733dd77a6d6696bb426b200b27adeb8be7e887a667Yuichiro Hanada    static final String BIGRAM_FREQ_CONTENT_ID = "_freq";
2749514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada    static final int BIGRAM_TIMESTAMP_SIZE = 4;
2759514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada    static final int BIGRAM_COUNTER_SIZE = 1;
2769514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada    static final int BIGRAM_LEVEL_SIZE = 1;
277a141d8ef7dcf8f942eb7bd4ca006f63da1744319Yuichiro Hanada
27873b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada    static final int SHORTCUT_CONTENT_COUNT = 1;
27973b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada    static final int SHORTCUT_CONTENT_INDEX = 0;
28073b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada    // With the English main dictionary as of October 2013, the size of shortcut address table is
2812fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    // 26KB with the block size being 64.
28273b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada    // This is only 4.4% of that of full address table.
28373b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada    static final int SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 64;
28473b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada    static final String SHORTCUT_CONTENT_ID = "_shortcut";
28573b9d3b879c109a7b8487b609b0715ffe3090142Yuichiro Hanada
28681d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE;
28781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int NO_PARENT_ADDRESS = 0;
288061d225fb1d110695b396a470d9ae6a9a3331003Yuichiro Hanada    static final int NO_FORWARD_LINK_ADDRESS = 0;
28981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int INVALID_CHARACTER = -1;
29081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
291576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT = 0x7F; // 127
292db4f3730047c8a3e25e031aacc07bb02bc47c5aeKeisuke Kuroyanagi    // Large PtNode array size field size is 2 bytes.
293db4f3730047c8a3e25e031aacc07bb02bc47c5aeKeisuke Kuroyanagi    static final int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG = 0x8000;
294576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int MAX_PTNODES_IN_A_PT_NODE_ARRAY = 0x7FFF; // 32767
295576f625ee1b22e26baab46cc4ad3138e901383e2Yuichiro Hanada    static final int MAX_BIGRAMS_IN_A_PTNODE = 10000;
296a141d8ef7dcf8f942eb7bd4ca006f63da1744319Yuichiro Hanada    static final int MAX_SHORTCUT_LIST_SIZE_IN_A_PTNODE = 0xFFFF;
29781d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
29881d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int MAX_TERMINAL_FREQUENCY = 255;
29981d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    static final int MAX_BIGRAM_FREQUENCY = 15;
30081d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada
30147cac57e4593f47e753410e4199e84e458d6de6fJean Chalard    public static final int SHORTCUT_WHITELIST_FREQUENCY = 15;
30247cac57e4593f47e753410e4199e84e458d6de6fJean Chalard
303d36245fad292ea660ca49f38a3ec36e07727dda5Yuichiro Hanada    // This option needs to be the same numeric value as the one in binary_format.h.
304d36245fad292ea660ca49f38a3ec36e07727dda5Yuichiro Hanada    static final int NOT_VALID_WORD = -99;
3058ec0064c49e80945dbe1bb31129eb890478b7e06Yuichiro Hanada    static final int SIGNED_CHILDREN_ADDRESS_SIZE = 3;
306d36245fad292ea660ca49f38a3ec36e07727dda5Yuichiro Hanada
307665592774c1d5ec90abbe772d4303fe8d8fe8089Yuichiro Hanada    static final int UINT8_MAX = 0xFF;
308665592774c1d5ec90abbe772d4303fe8d8fe8089Yuichiro Hanada    static final int UINT16_MAX = 0xFFFF;
309665592774c1d5ec90abbe772d4303fe8d8fe8089Yuichiro Hanada    static final int UINT24_MAX = 0xFFFFFF;
310665592774c1d5ec90abbe772d4303fe8d8fe8089Yuichiro Hanada    static final int SINT24_MAX = 0x7FFFFF;
311665592774c1d5ec90abbe772d4303fe8d8fe8089Yuichiro Hanada    static final int MSB8 = 0x80;
312665592774c1d5ec90abbe772d4303fe8d8fe8089Yuichiro Hanada    static final int MSB24 = 0x800000;
313665592774c1d5ec90abbe772d4303fe8d8fe8089Yuichiro Hanada
3141a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada    /**
3151a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada     * Options about file format.
3161a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada     */
317a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    public static final class FormatOptions {
3181a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada        public final int mVersion;
3199514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada        public final boolean mHasTimestamp;
3207d1ae52ded0deca6b2674df0273ec852ad36319fYuichiro Hanada
3217d1ae52ded0deca6b2674df0273ec852ad36319fYuichiro Hanada        @UsedForTesting
3227b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard        public FormatOptions(final int version) {
3237b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard            this(version, false /* hasTimestamp */);
3249514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada        }
3259514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada
3267b55cd3e2b4966150fa4c44dd43ebfeb77058a43Jean Chalard        public FormatOptions(final int version, final boolean hasTimestamp) {
3271a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada            mVersion = version;
3289514ed5c2a49e645e2d468f7191d54d77d9f127fYuichiro Hanada            mHasTimestamp = hasTimestamp;
3291a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada        }
3301a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada    }
3311a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada
3321a347723c5ad4a71076df67f3af3b702db205719Yuichiro Hanada    /**
3333ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi     * Options global to the dictionary.
3343ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi     */
3353ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi    public static final class DictionaryOptions {
3363ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi        public final HashMap<String, String> mAttributes;
3373ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi        public DictionaryOptions(final HashMap<String, String> attributes) {
3383ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            mAttributes = attributes;
3393ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi        }
3403ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi        @Override
3413ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi        public String toString() { // Convenience method
3423ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            return toString(0, false);
3433ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi        }
3443ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi        public String toString(final int indentCount, final boolean plumbing) {
3453ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            final StringBuilder indent = new StringBuilder();
3463ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            if (plumbing) {
3473ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                indent.append("H:");
3483ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            } else {
3493ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                for (int i = 0; i < indentCount; ++i) {
3503ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                    indent.append(" ");
3513ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                }
3523ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            }
3533ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            final StringBuilder s = new StringBuilder();
3543ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            for (final String optionKey : mAttributes.keySet()) {
3553ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                s.append(indent);
3563ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                s.append(optionKey);
3573ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                s.append(" = ");
3583ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                if ("date".equals(optionKey) && !plumbing) {
3593ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                    // Date needs a number of milliseconds, but the dictionary contains seconds
3603ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                    s.append(new Date(
3613ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                            1000 * Long.parseLong(mAttributes.get(optionKey))).toString());
3623ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                } else {
3633ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                    s.append(mAttributes.get(optionKey));
3643ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                }
3653ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi                s.append("\n");
3663ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            }
3673ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi            return s.toString();
3683ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi        }
3693ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi    }
3703ad4af2354e7003ac288dafe3600268fe860d752Keisuke Kuroyanagi
37181d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    private FormatSpec() {
37281d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada        // This utility class is not publicly instantiable.
37381d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada    }
37481d97eec0e77e72cce606f9c9f96091c0b348190Yuichiro Hanada}
375