]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/text/BreakDictionary.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / text / BreakDictionary.java
1 /*\r
2  *******************************************************************************\r
3  * Copyright (C) 1996-2010, International Business Machines Corporation and    *\r
4  * others. All Rights Reserved.                                                *\r
5  *******************************************************************************\r
6  */\r
7 package com.ibm.icu.text;\r
8 \r
9 import java.io.DataInputStream;\r
10 import java.io.FileInputStream;\r
11 import java.io.FileNotFoundException;\r
12 import java.io.FileOutputStream;\r
13 import java.io.IOException;\r
14 import java.io.InputStream;\r
15 import java.io.OutputStreamWriter;\r
16 import java.io.PrintWriter;\r
17 import java.io.UnsupportedEncodingException;\r
18 \r
19 import com.ibm.icu.util.CompactByteArray;\r
20 \r
21 /**\r
22  * This is the class that represents the list of known words used by\r
23  * DictionaryBasedBreakIterator.  The conceptual data structure used\r
24  * here is a trie: there is a node hanging off the root node for every\r
25  * letter that can start a word.  Each of these nodes has a node hanging\r
26  * off of it for every letter that can be the second letter of a word\r
27  * if this node is the first letter, and so on.  The trie is represented\r
28  * as a two-dimensional array that can be treated as a table of state\r
29  * transitions.  Indexes are used to compress this array, taking\r
30  * advantage of the fact that this array will always be very sparse.\r
31  */\r
32 class BreakDictionary {\r
33     //=================================================================================\r
34     // testing and debugging\r
35     //=================================================================================\r
36     ///CLOVER:OFF\r
37     //The main method looks like it was useful once but now seems worthless. It is not used by any method or class.\r
38     public static void main(String args[])\r
39             throws FileNotFoundException, UnsupportedEncodingException, IOException {\r
40         String filename = args[0];\r
41 \r
42         BreakDictionary dictionary = new BreakDictionary(new FileInputStream(filename));\r
43 \r
44         PrintWriter out = null;\r
45 \r
46         if(args.length >= 2) {\r
47             out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(args[1]), "UnicodeLittle"));\r
48         }\r
49 \r
50         dictionary.printWordList("", 0, out);\r
51 \r
52         if (out != null) {\r
53             out.close();\r
54         }\r
55     }\r
56     ///CLOVER:ON\r
57 \r
58     ///CLOVER:OFF\r
59     /* public */ void printWordList(String partialWord, int state, PrintWriter out)\r
60             throws IOException {\r
61         if (state == 0xFFFF) {\r
62             System.out.println(partialWord);\r
63             if (out != null) {\r
64                 out.println(partialWord);\r
65             }\r
66         }\r
67         else {\r
68             for (int i = 0; i < numCols; i++) {\r
69                 int newState = (at(state, i)) & 0xFFFF;\r
70 \r
71                 if (newState != 0) {\r
72                     char newChar = reverseColumnMap[i];\r
73                     String newPartialWord = partialWord;\r
74 \r
75                     if (newChar != 0) {\r
76                         newPartialWord += newChar;\r
77                     }\r
78 \r
79                     printWordList(newPartialWord, newState, out);\r
80                 }\r
81             }\r
82         }\r
83     }\r
84     ///CLOVER:ON\r
85 \r
86     /**\r
87      * A map used to go from column numbers to characters.  Used only\r
88      * for debugging right now.\r
89      */\r
90     private char[] reverseColumnMap = null;\r
91 \r
92     //=================================================================================\r
93     // data members\r
94     //=================================================================================\r
95 \r
96     /**\r
97      * Maps from characters to column numbers.  The main use of this is to\r
98      * avoid making room in the array for empty columns.\r
99      */\r
100     private CompactByteArray columnMap = null;\r
101 \r
102     /**\r
103      * The number of actual columns in the table\r
104      */\r
105     private int numCols;\r
106 \r
107     /*\r
108      * Columns are organized into groups of 32.  This says how many\r
109      * column groups.  (We could calculate this, but we store the\r
110      * value to avoid having to repeatedly calculate it.)\r
111      */\r
112     //private int numColGroups;\r
113 \r
114     /**\r
115      * The actual compressed state table.  Each conceptual row represents\r
116      * a state, and the cells in it contain the row numbers of the states\r
117      * to transition to for each possible letter.  0 is used to indicate\r
118      * an illegal combination of letters (i.e., the error state).  The\r
119      * table is compressed by eliminating all the unpopulated (i.e., zero)\r
120      * cells.  Multiple conceptual rows can then be doubled up in a single\r
121      * physical row by sliding them up and possibly shifting them to one\r
122      * side or the other so the populated cells don't collide.  Indexes\r
123      * are used to identify unpopulated cells and to locate populated cells.\r
124      */\r
125     private short[] table = null;\r
126 \r
127     /**\r
128      * This index maps logical row numbers to physical row numbers\r
129      */\r
130     private short[] rowIndex = null;\r
131 \r
132     /**\r
133      * A bitmap is used to tell which cells in the comceptual table are\r
134      * populated.  This array contains all the unique bit combinations\r
135      * in that bitmap.  If the table is more than 32 columns wide,\r
136      * successive entries in this array are used for a single row.\r
137      */\r
138     private int[] rowIndexFlags = null;\r
139 \r
140     /**\r
141      * This index maps from a logical row number into the bitmap table above.\r
142      * (This keeps us from storing duplicate bitmap combinations.)  Since there\r
143      * are a lot of rows with only one populated cell, instead of wasting space\r
144      * in the bitmap table, we just store a negative number in this index for\r
145      * rows with one populated cell.  The absolute value of that number is\r
146      * the column number of the populated cell.\r
147      */\r
148     private short[] rowIndexFlagsIndex = null;\r
149 \r
150     /**\r
151      * For each logical row, this index contains a constant that is added to\r
152      * the logical column number to get the physical column number\r
153      */\r
154     private byte[] rowIndexShifts = null;\r
155 \r
156     //=================================================================================\r
157     // deserialization\r
158     //=================================================================================\r
159 \r
160     /* public */ BreakDictionary(InputStream dictionaryStream) throws IOException {\r
161         readDictionaryFile(new DataInputStream(dictionaryStream));\r
162     }\r
163 \r
164     /* public */ void readDictionaryFile(DataInputStream in) throws IOException {\r
165         int l;\r
166 \r
167         // read in the version number (right now we just ignore it)\r
168         in.readInt();\r
169 \r
170         // read in the column map (this is serialized in its internal form:\r
171         // an index array followed by a data array)\r
172         l = in.readInt();\r
173         char[] temp = new char[l];\r
174         for (int i = 0; i < temp.length; i++)\r
175             temp[i] = (char)in.readShort();\r
176         l = in.readInt();\r
177         byte[] temp2 = new byte[l];\r
178         for (int i = 0; i < temp2.length; i++)\r
179             temp2[i] = in.readByte();\r
180         columnMap = new CompactByteArray(temp, temp2);\r
181 \r
182         // read in numCols and numColGroups\r
183         numCols = in.readInt();\r
184         /*numColGroups = */in.readInt();\r
185 \r
186         // read in the row-number index\r
187         l = in.readInt();\r
188         rowIndex = new short[l];\r
189         for (int i = 0; i < rowIndex.length; i++)\r
190             rowIndex[i] = in.readShort();\r
191 \r
192         // load in the populated-cells bitmap: index first, then bitmap list\r
193         l = in.readInt();\r
194         rowIndexFlagsIndex = new short[l];\r
195         for (int i = 0; i < rowIndexFlagsIndex.length; i++)\r
196             rowIndexFlagsIndex[i] = in.readShort();\r
197         l = in.readInt();\r
198         rowIndexFlags = new int[l];\r
199         for (int i = 0; i < rowIndexFlags.length; i++)\r
200             rowIndexFlags[i] = in.readInt();\r
201 \r
202         // load in the row-shift index\r
203         l = in.readInt();\r
204         rowIndexShifts = new byte[l];\r
205         for (int i = 0; i < rowIndexShifts.length; i++)\r
206             rowIndexShifts[i] = in.readByte();\r
207 \r
208         // finally, load in the actual state table\r
209         l = in.readInt();\r
210         table = new short[l];\r
211         for (int i = 0; i < table.length; i++)\r
212             table[i] = in.readShort();\r
213 \r
214         // this data structure is only necessary for testing and debugging purposes\r
215         reverseColumnMap = new char[numCols];\r
216         for (char c = 0; c < 0xffff; c++) {\r
217             int col = columnMap.elementAt(c);\r
218             if (col != 0) {\r
219                reverseColumnMap[col] = c;\r
220             }\r
221         }\r
222 \r
223         // close the stream\r
224         in.close();\r
225     }\r
226 \r
227     //=================================================================================\r
228     // access to the words\r
229     //=================================================================================\r
230 \r
231     /**\r
232      * Uses the column map to map the character to a column number, then\r
233      * passes the row and column number to the other version of at()\r
234      * @param row The current state\r
235      * @param ch The character whose column we're interested in\r
236      * @return The new state to transition to\r
237      */\r
238     /* public */ final short at(int row, char ch) {\r
239         int col = columnMap.elementAt(ch);\r
240         return at(row, col);\r
241     }\r
242 \r
243     /**\r
244      * Returns the value in the cell with the specified (logical) row and\r
245      * column numbers.  In DictionaryBasedBreakIterator, the row number is\r
246      * a state number, the column number is an input, and the return value\r
247      * is the row number of the new state to transition to.  (0 is the\r
248      * "error" state, and -1 is the "end of word" state in a dictionary)\r
249      * @param row The row number of the current state\r
250      * @param col The column number of the input character (0 means "not a\r
251      * dictionary character")\r
252      * @return The row number of the new state to transition to\r
253      */\r
254     /* public */ final short at(int row, int col) {\r
255         if (cellIsPopulated(row, col)) {\r
256             // we map from logical to physical row number by looking up the\r
257             // mapping in rowIndex; we map from logical column number to\r
258             // physical column number by looking up a shift value for this\r
259             // logical row and offsetting the logical column number by\r
260             // the shift amount.  Then we can use internalAt() to actually\r
261             // get the value out of the table.\r
262             return internalAt(rowIndex[row], col + rowIndexShifts[row]);\r
263         }\r
264         else {\r
265             return 0;\r
266         }\r
267     }\r
268 \r
269     /**\r
270      * Given (logical) row and column numbers, returns true if the\r
271      * cell in that position is populated\r
272      */\r
273     private final boolean cellIsPopulated(int row, int col) {\r
274         // look up the entry in the bitmap index for the specified row.\r
275         // If it's a negative number, it's the column number of the only\r
276         // populated cell in the row\r
277         if (rowIndexFlagsIndex[row] < 0) {\r
278             return col == -rowIndexFlagsIndex[row];\r
279         }\r
280 \r
281         // if it's a positive number, it's the offset of an entry in the bitmap\r
282         // list.  If the table is more than 32 columns wide, the bitmap is stored\r
283         // successive entries in the bitmap list, so we have to divide the column\r
284         // number by 32 and offset the number we got out of the index by the result.\r
285         // Once we have the appropriate piece of the bitmap, test the appropriate\r
286         // bit and return the result.\r
287         else {\r
288             int flags = rowIndexFlags[rowIndexFlagsIndex[row] + (col >> 5)];\r
289             return (flags & (1 << (col & 0x1f))) != 0;\r
290         }\r
291     }\r
292 \r
293     /**\r
294      * Implementation of at() when we know the specified cell is populated.\r
295      * @param row The PHYSICAL row number of the cell\r
296      * @param col The PHYSICAL column number of the cell\r
297      * @return The value stored in the cell\r
298      */\r
299     private final short internalAt(int row, int col) {\r
300         // the table is a one-dimensional array, so this just does the math necessary\r
301         // to treat it as a two-dimensional array (we don't just use a two-dimensional\r
302         // array because two-dimensional arrays are inefficient in Java)\r
303         return table[row * numCols + col];\r
304     }\r
305 }\r
306 \r