1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.latin.makedict; 18 19 import com.android.inputmethod.latin.Constants; 20 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions; 21 22 /** 23 * Dictionary File Format Specification. 24 */ 25 public final class FormatSpec { 26 27 /* 28 * Array of Node(FusionDictionary.Node) layout is as follows: 29 * 30 * g | 31 * r | the number of groups, 1 or 2 bytes. 32 * o | 1 byte = bbbbbbbb match 33 * u | case 1xxxxxxx => xxxxxxx << 8 + next byte 34 * p | otherwise => bbbbbbbb 35 * c | 36 * ount 37 * 38 * g | 39 * r | sequence of groups, 40 * o | the layout of each group is described below. 41 * u | 42 * ps 43 * 44 * f | 45 * o | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header) 46 * r | forward link address, 3byte 47 * w | 1 byte = bbbbbbbb match 48 * a | case 1xxxxxxx => -((xxxxxxx << 16) + (next byte << 8) + next byte) 49 * r | otherwise => (xxxxxxx << 16) + (next byte << 8) + next byte 50 * d | 51 * linkaddress 52 */ 53 54 /* Node(CharGroup) layout is as follows: 55 * | IF !SUPPORTS_DYNAMIC_UPDATE 56 * | addressType xx : mask with MASK_GROUP_ADDRESS_TYPE 57 * | 2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS 58 * f | 01 = 1 byte : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE 59 * l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES 60 * a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES 61 * g | ELSE 62 * s | is moved ? 2 bits, 11 = no : FLAG_IS_NOT_MOVED 63 * | This must be the same as FLAG_GROUP_ADDRESS_TYPE_THREEBYTES 64 * | 01 = yes : FLAG_IS_MOVED 65 * | the new address is stored in the same place as the parent address 66 * | is deleted? 10 = yes : FLAG_IS_DELETED 67 * | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS 68 * | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL 69 * | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS 70 * | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS 71 * | is not a word ? 1 bit, 1 = yes, 0 = no : FLAG_IS_NOT_A_WORD 72 * | is blacklisted ? 1 bit, 1 = yes, 0 = no : FLAG_IS_BLACKLISTED 73 * 74 * p | 75 * a | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header) 76 * r | parent address, 3byte 77 * e | 1 byte = bbbbbbbb match 78 * n | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte) 79 * t | otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte 80 * a | 81 * ddress 82 * 83 * c | IF FLAG_HAS_MULTIPLE_CHARS 84 * h | char, char, char, char n * (1 or 3 bytes) : use CharGroupInfo for i/o helpers 85 * a | end 1 byte, = 0 86 * r | ELSE 87 * s | char 1 or 3 bytes 88 * | END 89 * 90 * f | 91 * r | IF FLAG_IS_TERMINAL 92 * e | frequency 1 byte 93 * q | 94 * 95 * c | IF 00 = FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = addressType 96 * h | // nothing 97 * i | ELSIF 01 = FLAG_GROUP_ADDRESS_TYPE_ONEBYTE == addressType 98 * l | children address, 1 byte 99 * d | ELSIF 10 = FLAG_GROUP_ADDRESS_TYPE_TWOBYTES == addressType 100 * r | children address, 2 bytes 101 * e | ELSE // 11 = FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = addressType 102 * n | children address, 3 bytes 103 * A | END 104 * d 105 * dress 106 * 107 * | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS 108 * | shortcut string list 109 * | IF FLAG_IS_TERMINAL && FLAG_HAS_BIGRAMS 110 * | bigrams address list 111 * 112 * Char format is: 113 * 1 byte = bbbbbbbb match 114 * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte 115 * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because 116 * unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with 117 * 00011111 would be outside unicode. 118 * else: iso-latin-1 code 119 * This allows for the whole unicode range to be encoded, including chars outside of 120 * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control 121 * characters which should never happen anyway (and still work, but take 3 bytes). 122 * 123 * bigram address list is: 124 * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT 125 * | addressSign = 1 bit, : FLAG_ATTRIBUTE_OFFSET_NEGATIVE 126 * | 1 = must take -address, 0 = must take +address 127 * | xx : mask with MASK_ATTRIBUTE_ADDRESS_TYPE 128 * | addressFormat = 2 bits, 00 = unused : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE 129 * | 01 = 1 byte : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE 130 * | 10 = 2 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES 131 * | 11 = 3 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES 132 * | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY 133 * <address> | IF (01 == FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE == addressFormat) 134 * | read 1 byte, add top 4 bits 135 * | ELSIF (10 == FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES == addressFormat) 136 * | read 2 bytes, add top 4 bits 137 * | ELSE // 11 == FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES == addressFormat 138 * | read 3 bytes, add top 4 bits 139 * | END 140 * | if (FLAG_ATTRIBUTE_OFFSET_NEGATIVE) then address = -address 141 * if (FLAG_ATTRIBUTE_HAS_NEXT) goto bigram_and_shortcut_address_list_is 142 * 143 * shortcut string list is: 144 * <byte size> = GROUP_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes. 145 * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT 146 * | reserved = 3 bits, must be 0 147 * | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY 148 * <shortcut> = | string of characters at the char format described above, with the terminator 149 * | used to signal the end of the string. 150 * if (FLAG_ATTRIBUTE_HAS_NEXT goto flags 151 */ 152 153 static final int VERSION_1_MAGIC_NUMBER = 0x78B1; 154 public static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE; 155 static final int MINIMUM_SUPPORTED_VERSION = 1; 156 static final int MAXIMUM_SUPPORTED_VERSION = 3; 157 static final int NOT_A_VERSION_NUMBER = -1; 158 static final int FIRST_VERSION_WITH_HEADER_SIZE = 2; 159 static final int FIRST_VERSION_WITH_DYNAMIC_UPDATE = 3; 160 161 // These options need to be the same numeric values as the one in the native reading code. 162 static final int GERMAN_UMLAUT_PROCESSING_FLAG = 0x1; 163 // TODO: Make the native reading code read this variable. 164 static final int SUPPORTS_DYNAMIC_UPDATE = 0x2; 165 static final int FRENCH_LIGATURE_PROCESSING_FLAG = 0x4; 166 static final int CONTAINS_BIGRAMS_FLAG = 0x8; 167 168 // TODO: Make this value adaptative to content data, store it in the header, and 169 // use it in the reading code. 170 static final int MAX_WORD_LENGTH = Constants.Dictionary.MAX_WORD_LENGTH; 171 172 static final int PARENT_ADDRESS_SIZE = 3; 173 static final int FORWARD_LINK_ADDRESS_SIZE = 3; 174 175 // These flags are used only in the static dictionary. 176 static final int MASK_GROUP_ADDRESS_TYPE = 0xC0; 177 static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00; 178 static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40; 179 static final int FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80; 180 static final int FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0; 181 182 static final int FLAG_HAS_MULTIPLE_CHARS = 0x20; 183 184 static final int FLAG_IS_TERMINAL = 0x10; 185 static final int FLAG_HAS_SHORTCUT_TARGETS = 0x08; 186 static final int FLAG_HAS_BIGRAMS = 0x04; 187 static final int FLAG_IS_NOT_A_WORD = 0x02; 188 static final int FLAG_IS_BLACKLISTED = 0x01; 189 190 // These flags are used only in the dynamic dictionary. 191 static final int MASK_MOVE_AND_DELETE_FLAG = 0xC0; 192 static final int FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE = 0x40; 193 static final int FLAG_IS_MOVED = 0x00 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE; 194 static final int FLAG_IS_NOT_MOVED = 0x80 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE; 195 static final int FLAG_IS_DELETED = 0x80; 196 197 static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80; 198 static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40; 199 static final int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30; 200 static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10; 201 static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20; 202 static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30; 203 static final int FLAG_ATTRIBUTE_FREQUENCY = 0x0F; 204 205 static final int GROUP_CHARACTERS_TERMINATOR = 0x1F; 206 207 static final int GROUP_TERMINATOR_SIZE = 1; 208 static final int GROUP_FLAGS_SIZE = 1; 209 static final int GROUP_FREQUENCY_SIZE = 1; 210 static final int GROUP_MAX_ADDRESS_SIZE = 3; 211 static final int GROUP_ATTRIBUTE_FLAGS_SIZE = 1; 212 static final int GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE = 3; 213 static final int GROUP_SHORTCUT_LIST_SIZE_SIZE = 2; 214 215 static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE; 216 static final int NO_PARENT_ADDRESS = 0; 217 static final int NO_FORWARD_LINK_ADDRESS = 0; 218 static final int INVALID_CHARACTER = -1; 219 220 static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127 221 static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767 222 static final int MAX_BIGRAMS_IN_A_GROUP = 10000; 223 224 static final int MAX_TERMINAL_FREQUENCY = 255; 225 static final int MAX_BIGRAM_FREQUENCY = 15; 226 227 public static final int SHORTCUT_WHITELIST_FREQUENCY = 15; 228 229 // This option needs to be the same numeric value as the one in binary_format.h. 230 static final int NOT_VALID_WORD = -99; 231 static final int SIGNED_CHILDREN_ADDRESS_SIZE = 3; 232 233 /** 234 * Options about file format. 235 */ 236 public static final class FormatOptions { 237 public final int mVersion; 238 public final boolean mSupportsDynamicUpdate; 239 public FormatOptions(final int version) { 240 this(version, false); 241 } 242 public FormatOptions(final int version, final boolean supportsDynamicUpdate) { 243 mVersion = version; 244 if (version < FIRST_VERSION_WITH_DYNAMIC_UPDATE && supportsDynamicUpdate) { 245 throw new RuntimeException("Dynamic updates are only supported with versions " 246 + FIRST_VERSION_WITH_DYNAMIC_UPDATE + " and ulterior."); 247 } 248 mSupportsDynamicUpdate = supportsDynamicUpdate; 249 } 250 } 251 252 /** 253 * Class representing file header. 254 */ 255 public static final class FileHeader { 256 public final int mHeaderSize; 257 public final DictionaryOptions mDictionaryOptions; 258 public final FormatOptions mFormatOptions; 259 private static final String DICTIONARY_VERSION_ATTRIBUTE = "version"; 260 private static final String DICTIONARY_LOCALE_ATTRIBUTE = "locale"; 261 private static final String DICTIONARY_ID_ATTRIBUTE = "dictionary"; 262 private static final String DICTIONARY_DESCRIPTION_ATTRIBUTE = "description"; 263 public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions, 264 final FormatOptions formatOptions) { 265 mHeaderSize = headerSize; 266 mDictionaryOptions = dictionaryOptions; 267 mFormatOptions = formatOptions; 268 } 269 270 // Helper method to get the locale as a String 271 public String getLocaleString() { 272 return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE); 273 } 274 275 // Helper method to get the version String 276 public String getVersion() { 277 return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_VERSION_ATTRIBUTE); 278 } 279 280 // Helper method to get the dictionary ID as a String 281 public String getId() { 282 return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_ID_ATTRIBUTE); 283 } 284 285 // Helper method to get the description 286 public String getDescription() { 287 // TODO: Right now each dictionary file comes with a description in its own language. 288 // It will display as is no matter the device's locale. It should be internationalized. 289 return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_DESCRIPTION_ATTRIBUTE); 290 } 291 } 292 293 private FormatSpec() { 294 // This utility class is not publicly instantiable. 295 } 296 } 297