-
Notifications
You must be signed in to change notification settings - Fork 670
/
Copy pathBreakDictionary.java
306 lines (274 loc) · 12.1 KB
/
BreakDictionary.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
* (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
*
* The original version of this source code and documentation
* is copyrighted and owned by Taligent, Inc., a wholly-owned
* subsidiary of IBM. These materials are provided under terms
* of a License Agreement between Taligent and Sun. This technology
* is protected by multiple US and International patents.
*
* This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*/
package sun.text;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.MissingResourceException;
import sun.text.CompactByteArray;
import sun.text.SupplementaryCharacterData;
/**
* This is the class that represents the list of known words used by
* DictionaryBasedBreakIterator. The conceptual data structure used
* here is a trie: there is a node hanging off the root node for every
* letter that can start a word. Each of these nodes has a node hanging
* off of it for every letter that can be the second letter of a word
* if this node is the first letter, and so on. The trie is represented
* as a two-dimensional array that can be treated as a table of state
* transitions. Indexes are used to compress this array, taking
* advantage of the fact that this array will always be very sparse.
*/
class BreakDictionary {
//=========================================================================
// data members
//=========================================================================
/**
* The version of the dictionary that was read in.
*/
private static int supportedVersion = 1;
/**
* Maps from characters to column numbers. The main use of this is to
* avoid making room in the array for empty columns.
*/
private CompactByteArray columnMap = null;
private SupplementaryCharacterData supplementaryCharColumnMap = null;
/**
* The number of actual columns in the table
*/
private int numCols;
/**
* Columns are organized into groups of 32. This says how many
* column groups. (We could calculate this, but we store the
* value to avoid having to repeatedly calculate it.)
*/
private int numColGroups;
/**
* The actual compressed state table. Each conceptual row represents
* a state, and the cells in it contain the row numbers of the states
* to transition to for each possible letter. 0 is used to indicate
* an illegal combination of letters (i.e., the error state). The
* table is compressed by eliminating all the unpopulated (i.e., zero)
* cells. Multiple conceptual rows can then be doubled up in a single
* physical row by sliding them up and possibly shifting them to one
* side or the other so the populated cells don't collide. Indexes
* are used to identify unpopulated cells and to locate populated cells.
*/
private short[] table = null;
/**
* This index maps logical row numbers to physical row numbers
*/
private short[] rowIndex = null;
/**
* A bitmap is used to tell which cells in the comceptual table are
* populated. This array contains all the unique bit combinations
* in that bitmap. If the table is more than 32 columns wide,
* successive entries in this array are used for a single row.
*/
private int[] rowIndexFlags = null;
/**
* This index maps from a logical row number into the bitmap table above.
* (This keeps us from storing duplicate bitmap combinations.) Since there
* are a lot of rows with only one populated cell, instead of wasting space
* in the bitmap table, we just store a negative number in this index for
* rows with one populated cell. The absolute value of that number is
* the column number of the populated cell.
*/
private short[] rowIndexFlagsIndex = null;
/**
* For each logical row, this index contains a constant that is added to
* the logical column number to get the physical column number
*/
private byte[] rowIndexShifts = null;
//=========================================================================
// deserialization
//=========================================================================
BreakDictionary(String dictionaryName, byte[] dictionaryData) {
try {
setupDictionary(dictionaryName, dictionaryData);
} catch (BufferUnderflowException bue) {
MissingResourceException e;
e = new MissingResourceException("Corrupted dictionary data",
dictionaryName, "");
e.initCause(bue);
throw e;
}
}
private void setupDictionary(String dictionaryName, byte[] dictionaryData) {
ByteBuffer bb = ByteBuffer.wrap(dictionaryData);
// check version
int version = bb.getInt();
if (version != supportedVersion) {
throw new MissingResourceException("Dictionary version(" + version + ") is unsupported",
dictionaryName, "");
}
// Check data size
int len = bb.getInt();
if (bb.position() + len != bb.limit()) {
throw new MissingResourceException("Dictionary size is wrong: " + bb.limit(),
dictionaryName, "");
}
// read in the column map for BMP characteres (this is serialized in
// its internal form: an index array followed by a data array)
len = bb.getInt();
short[] temp = new short[len];
for (int i = 0; i < len; i++) {
temp[i] = bb.getShort();
}
len = bb.getInt();
byte[] temp2 = new byte[len];
bb.get(temp2);
columnMap = new CompactByteArray(temp, temp2);
// read in numCols and numColGroups
numCols = bb.getInt();
numColGroups = bb.getInt();
// read in the row-number index
len = bb.getInt();
rowIndex = new short[len];
for (int i = 0; i < len; i++) {
rowIndex[i] = bb.getShort();
}
// load in the populated-cells bitmap: index first, then bitmap list
len = bb.getInt();
rowIndexFlagsIndex = new short[len];
for (int i = 0; i < len; i++) {
rowIndexFlagsIndex[i] = bb.getShort();
}
len = bb.getInt();
rowIndexFlags = new int[len];
for (int i = 0; i < len; i++) {
rowIndexFlags[i] = bb.getInt();
}
// load in the row-shift index
len = bb.getInt();
rowIndexShifts = new byte[len];
bb.get(rowIndexShifts);
// load in the actual state table
len = bb.getInt();
table = new short[len];
for (int i = 0; i < len; i++) {
table[i] = bb.getShort();
}
// finally, prepare the column map for supplementary characters
len = bb.getInt();
int[] temp3 = new int[len];
for (int i = 0; i < len; i++) {
temp3[i] = bb.getInt();
}
assert bb.position() == bb.limit();
supplementaryCharColumnMap = new SupplementaryCharacterData(temp3);
}
//=========================================================================
// access to the words
//=========================================================================
/**
* Uses the column map to map the character to a column number, then
* passes the row and column number to getNextState()
* @param row The current state
* @param ch The character whose column we're interested in
* @return The new state to transition to
*/
public final short getNextStateFromCharacter(int row, int ch) {
int col;
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
col = columnMap.elementAt((char)ch);
} else {
col = supplementaryCharColumnMap.getValue(ch);
}
return getNextState(row, col);
}
/**
* Returns the value in the cell with the specified (logical) row and
* column numbers. In DictionaryBasedBreakIterator, the row number is
* a state number, the column number is an input, and the return value
* is the row number of the new state to transition to. (0 is the
* "error" state, and -1 is the "end of word" state in a dictionary)
* @param row The row number of the current state
* @param col The column number of the input character (0 means "not a
* dictionary character")
* @return The row number of the new state to transition to
*/
public final short getNextState(int row, int col) {
if (cellIsPopulated(row, col)) {
// we map from logical to physical row number by looking up the
// mapping in rowIndex; we map from logical column number to
// physical column number by looking up a shift value for this
// logical row and offsetting the logical column number by
// the shift amount. Then we can use internalAt() to actually
// get the value out of the table.
return internalAt(rowIndex[row], col + rowIndexShifts[row]);
}
else {
return 0;
}
}
/**
* Given (logical) row and column numbers, returns true if the
* cell in that position is populated
*/
private boolean cellIsPopulated(int row, int col) {
// look up the entry in the bitmap index for the specified row.
// If it's a negative number, it's the column number of the only
// populated cell in the row
if (rowIndexFlagsIndex[row] < 0) {
return col == -rowIndexFlagsIndex[row];
}
// if it's a positive number, it's the offset of an entry in the bitmap
// list. If the table is more than 32 columns wide, the bitmap is stored
// successive entries in the bitmap list, so we have to divide the column
// number by 32 and offset the number we got out of the index by the result.
// Once we have the appropriate piece of the bitmap, test the appropriate
// bit and return the result.
else {
int flags = rowIndexFlags[rowIndexFlagsIndex[row] + (col >> 5)];
return (flags & (1 << (col & 0x1f))) != 0;
}
}
/**
* Implementation of getNextState() when we know the specified cell is
* populated.
* @param row The PHYSICAL row number of the cell
* @param col The PHYSICAL column number of the cell
* @return The value stored in the cell
*/
private short internalAt(int row, int col) {
// the table is a one-dimensional array, so this just does the math necessary
// to treat it as a two-dimensional array (we don't just use a two-dimensional
// array because two-dimensional arrays are inefficient in Java)
return table[row * numCols + col];
}
}