]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/TrieIterator.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / TrieIterator.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 \r
8 package com.ibm.icu.impl;\r
9 \r
10 import java.util.NoSuchElementException;\r
11 \r
12 import com.ibm.icu.lang.UCharacter;\r
13 import com.ibm.icu.text.UTF16;\r
14 import com.ibm.icu.util.RangeValueIterator;\r
15 \r
16 /**\r
17  * <p>Class enabling iteration of the values in a Trie.</p>\r
18  * <p>Result of each iteration contains the interval of codepoints that have\r
19  * the same value type and the value type itself.</p>\r
20  * <p>The comparison of each codepoint value is done via extract(), which the\r
21  * default implementation is to return the value as it is.</p> \r
22  * <p>Method extract() can be overwritten to perform manipulations on \r
23  * codepoint values in order to perform specialized comparison.</p>\r
24  * <p>TrieIterator is designed to be a generic iterator for the CharTrie\r
25  * and the IntTrie, hence to accommodate both types of data, the return \r
26  * result will be in terms of int (32 bit) values.</p>\r
27  * <p>See com.ibm.icu.text.UCharacterTypeIterator for examples of use.</p>\r
28  * <p>Notes for porting utrie_enum from icu4c to icu4j:<br>\r
29  * Internally, icu4c's utrie_enum performs all iterations in its body. In Java\r
30  * sense, the caller will have to pass a object with a callback function \r
31  * UTrieEnumRange(const void *context, UChar32 start, UChar32 limit, \r
32  * uint32_t value) into utrie_enum. utrie_enum will then find ranges of \r
33  * codepoints with the same value as determined by \r
34  * UTrieEnumValue(const void *context, uint32_t value). for each range, \r
35  * utrie_enum calls the callback function to perform a task. In this way,\r
36  * icu4c performs the iteration within utrie_enum.\r
37  * To follow the JDK model, icu4j is slightly different from icu4c.\r
38  * Instead of requesting the caller to implement an object for a callback.\r
39  * The caller will have to implement a subclass of TrieIterator, fleshing out\r
40  * the method extract(int) (equivalent to UTrieEnumValue). Independent of icu4j, \r
41  * the caller will have to code his own iteration and flesh out the task \r
42  * (equivalent to UTrieEnumRange) to be performed in the iteration loop.\r
43  * </p>\r
44  * <p>There are basically 3 usage scenarios for porting:</p>\r
45  * <p>1) UTrieEnumValue is the only implemented callback then just implement a \r
46  * subclass of TrieIterator and override the extract(int) method. The \r
47  * extract(int) method is analogus to UTrieEnumValue callback.\r
48  * </p>\r
49  * <p>2) UTrieEnumValue and UTrieEnumRange both are implemented then implement \r
50  * a subclass of TrieIterator, override the extract method and iterate, e.g\r
51  * </p>\r
52  * <p>utrie_enum(&normTrie, _enumPropertyStartsValue, _enumPropertyStartsRange, \r
53  *               set);<br>\r
54  * In Java :<br>\r
55  * <pre>\r
56  * class TrieIteratorImpl extends TrieIterator{\r
57  *     public TrieIteratorImpl(Trie data){\r
58  *         super(data);\r
59  *     }\r
60  *     public int extract(int value){\r
61  *         // port the implementation of _enumPropertyStartsValue here\r
62  *     }\r
63  * }\r
64  * .... \r
65  * TrieIterator fcdIter  = new TrieIteratorImpl(fcdTrieImpl.fcdTrie);\r
66  * while(fcdIter.next(result)) {\r
67  *     // port the implementation of _enumPropertyStartsRange\r
68  * }\r
69  * </pre>\r
70  * </p>\r
71  * <p>3) UTrieEnumRange is the only implemented callback then just implement \r
72  * the while loop, when utrie_enum is called\r
73  * <pre>\r
74  * // utrie_enum(&fcdTrie, NULL, _enumPropertyStartsRange, set);\r
75  * TrieIterator fcdIter  = new TrieIterator(fcdTrieImpl.fcdTrie);\r
76  * while(fcdIter.next(result)){\r
77  *     set.add(result.start);\r
78  * }\r
79  * </p>\r
80  * @author synwee\r
81  * @see com.ibm.icu.impl.Trie\r
82  * @since release 2.1, Jan 17 2002\r
83  */\r
84 public class TrieIterator implements RangeValueIterator\r
85 \r
86 {\r
87     // public constructor ---------------------------------------------\r
88     \r
89     /**\r
90     * TrieEnumeration constructor\r
91     * @param trie to be used\r
92     * @exception IllegalArgumentException throw when argument is null.\r
93     */\r
94     public TrieIterator(Trie trie)\r
95     {\r
96         if (trie == null) {\r
97             throw new IllegalArgumentException(\r
98                                           "Argument trie cannot be null");\r
99         }\r
100         m_trie_             = trie;\r
101         // synwee: check that extract belongs to the child class\r
102         m_initialValue_     = extract(m_trie_.getInitialValue());\r
103         reset();\r
104     }\r
105     \r
106     // public methods -------------------------------------------------\r
107     \r
108     /**\r
109     * <p>Returns true if we are not at the end of the iteration, false \r
110     * otherwise.</p>\r
111     * <p>The next set of codepoints with the same value type will be \r
112     * calculated during this call and returned in the arguement element.</p>\r
113     * @param element return result \r
114     * @return true if we are not at the end of the iteration, false otherwise.\r
115     * @exception NoSuchElementException - if no more elements exist.\r
116     * @see com.ibm.icu.util.RangeValueIterator.Element\r
117     */\r
118     public final boolean next(Element element)\r
119     {\r
120         if (m_nextCodepoint_ > UCharacter.MAX_VALUE) {\r
121             return false;\r
122         }\r
123         if (m_nextCodepoint_ < UCharacter.SUPPLEMENTARY_MIN_VALUE &&\r
124             calculateNextBMPElement(element)) {\r
125             return true;\r
126         }    \r
127         calculateNextSupplementaryElement(element);\r
128         return true;\r
129     }\r
130      \r
131     /**\r
132     * Resets the iterator to the beginning of the iteration\r
133     */\r
134     public final void reset()\r
135     {\r
136         m_currentCodepoint_ = 0;\r
137         m_nextCodepoint_    = 0;\r
138         m_nextIndex_        = 0;\r
139         m_nextBlock_ = m_trie_.m_index_[0] << Trie.INDEX_STAGE_2_SHIFT_;\r
140         if (m_nextBlock_ == m_trie_.m_dataOffset_) {\r
141             m_nextValue_ = m_initialValue_;\r
142         }\r
143         else {\r
144             m_nextValue_ = extract(m_trie_.getValue(m_nextBlock_));\r
145         }\r
146         m_nextBlockIndex_ = 0;\r
147         m_nextTrailIndexOffset_ = TRAIL_SURROGATE_INDEX_BLOCK_LENGTH_;\r
148     }\r
149     \r
150     // protected methods ----------------------------------------------\r
151     \r
152     /**\r
153     * Called by next() to extracts a 32 bit value from a trie value\r
154     * used for comparison.\r
155     * This method is to be overwritten if special manipulation is to be done\r
156     * to retrieve a relevant comparison.\r
157     * The default function is to return the value as it is.\r
158     * @param value a value from the trie\r
159     * @return extracted value\r
160     */\r
161     protected int extract(int value)\r
162     {\r
163         return value;\r
164     }\r
165     \r
166     // private methods ------------------------------------------------\r
167     \r
168     /**\r
169     * Set the result values\r
170     * @param element return result object\r
171     * @param start codepoint of range \r
172     * @param limit (end + 1) codepoint of range\r
173     * @param value common value of range\r
174     */\r
175     private final void setResult(Element element, int start, int limit, \r
176                                  int value)\r
177     {\r
178         element.start = start;\r
179         element.limit = limit;\r
180         element.value = value;\r
181     }\r
182     \r
183     /**\r
184     * Finding the next element.\r
185     * This method is called just before returning the result of \r
186     * next().\r
187     * We always store the next element before it is requested.\r
188     * In the case that we have to continue calculations into the \r
189     * supplementary planes, a false will be returned.\r
190     * @param element return result object\r
191     * @return true if the next range is found, false if we have to proceed to\r
192     *         the supplementary range.\r
193     */\r
194     private final boolean calculateNextBMPElement(Element element)\r
195     {\r
196         int currentValue    = m_nextValue_;\r
197         m_currentCodepoint_ = m_nextCodepoint_;\r
198         m_nextCodepoint_ ++;\r
199         m_nextBlockIndex_ ++;\r
200         if (!checkBlockDetail(currentValue)) {\r
201             setResult(element, m_currentCodepoint_, m_nextCodepoint_, \r
202                       currentValue);\r
203             return true;\r
204         }\r
205         // synwee check that next block index == 0 here \r
206         // enumerate BMP - the main loop enumerates data blocks\r
207         while (m_nextCodepoint_ < UCharacter.SUPPLEMENTARY_MIN_VALUE) {\r
208             // because of the way the character is split to form the index\r
209             // the lead surrogate and trail surrogate can not be in the\r
210             // mid of a block\r
211             if (m_nextCodepoint_ == LEAD_SURROGATE_MIN_VALUE_) {\r
212                 // skip lead surrogate code units,\r
213                 // go to lead surrogate codepoints\r
214                 m_nextIndex_ = BMP_INDEX_LENGTH_;\r
215             }\r
216             else if (m_nextCodepoint_ == TRAIL_SURROGATE_MIN_VALUE_) {\r
217                 // go back to regular BMP code points\r
218                 m_nextIndex_ = m_nextCodepoint_ >> Trie.INDEX_STAGE_1_SHIFT_;\r
219             } else {\r
220                 m_nextIndex_ ++;\r
221             }\r
222             \r
223             m_nextBlockIndex_ = 0;\r
224             if (!checkBlock(currentValue)) {\r
225                 setResult(element, m_currentCodepoint_, m_nextCodepoint_, \r
226                           currentValue);\r
227                 return true;\r
228             }\r
229         }\r
230         m_nextCodepoint_ --;   // step one back since this value has not been\r
231         m_nextBlockIndex_ --;  // retrieved yet.\r
232         return false;\r
233     }\r
234 \r
235     /**\r
236     * Finds the next supplementary element.\r
237     * For each entry in the trie, the value to be delivered is passed through\r
238     * extract().\r
239     * We always store the next element before it is requested.\r
240     * Called after calculateNextBMP() completes its round of BMP characters.\r
241     * There is a slight difference in the usage of m_currentCodepoint_\r
242     * here as compared to calculateNextBMP(). Though both represents the\r
243     * lower bound of the next element, in calculateNextBMP() it gets set\r
244     * at the start of any loop, where-else, in calculateNextSupplementary()\r
245     * since m_currentCodepoint_ already contains the lower bound of the\r
246     * next element (passed down from calculateNextBMP()), we keep it till \r
247     * the end before resetting it to the new value.\r
248     * Note, if there are no more iterations, it will never get to here. \r
249     * Blocked out by next().\r
250     * @param element return result object\r
251     */\r
252     private final void calculateNextSupplementaryElement(Element element)\r
253     {\r
254         int currentValue = m_nextValue_;\r
255         m_nextCodepoint_ ++;\r
256         m_nextBlockIndex_ ++;\r
257         \r
258         if (UTF16.getTrailSurrogate(m_nextCodepoint_) \r
259                                         != UTF16.TRAIL_SURROGATE_MIN_VALUE) { \r
260             // this piece is only called when we are in the middle of a lead\r
261             // surrogate block\r
262             if (!checkNullNextTrailIndex() && !checkBlockDetail(currentValue)) {\r
263                 setResult(element, m_currentCodepoint_, m_nextCodepoint_, \r
264                           currentValue);\r
265                 m_currentCodepoint_ = m_nextCodepoint_;\r
266                 return;\r
267             }\r
268             // we have cleared one block\r
269             m_nextIndex_ ++;\r
270             m_nextTrailIndexOffset_ ++;\r
271             if (!checkTrailBlock(currentValue)) {\r
272                 setResult(element, m_currentCodepoint_, m_nextCodepoint_, \r
273                           currentValue);\r
274                 m_currentCodepoint_ = m_nextCodepoint_;\r
275                 return;\r
276             }\r
277         }\r
278         int nextLead  = UTF16.getLeadSurrogate(m_nextCodepoint_);\r
279         // enumerate supplementary code points\r
280         while (nextLead < TRAIL_SURROGATE_MIN_VALUE_) {\r
281             // lead surrogate access\r
282             final int leadBlock = \r
283                    m_trie_.m_index_[nextLead >> Trie.INDEX_STAGE_1_SHIFT_] << \r
284                                                    Trie.INDEX_STAGE_2_SHIFT_;\r
285             if (leadBlock == m_trie_.m_dataOffset_) {\r
286                 // no entries for a whole block of lead surrogates\r
287                 if (currentValue != m_initialValue_) {\r
288                     m_nextValue_      = m_initialValue_;\r
289                     m_nextBlock_      = leadBlock;  // == m_trie_.m_dataOffset_\r
290                     m_nextBlockIndex_ = 0;\r
291                     setResult(element, m_currentCodepoint_, m_nextCodepoint_, \r
292                               currentValue);\r
293                     m_currentCodepoint_ = m_nextCodepoint_;\r
294                     return;\r
295                 }\r
296 \r
297                 nextLead += DATA_BLOCK_LENGTH_;\r
298                 // number of total affected supplementary codepoints in one\r
299                 // block\r
300                 // this is not a simple addition of \r
301                 // DATA_BLOCK_SUPPLEMENTARY_LENGTH since we need to consider\r
302                 // that we might have moved some of the codepoints\r
303                 m_nextCodepoint_ = UCharacterProperty.getRawSupplementary(\r
304                                      (char)nextLead, \r
305                                      (char)UTF16.TRAIL_SURROGATE_MIN_VALUE);\r
306                 continue;\r
307             }\r
308             if (m_trie_.m_dataManipulate_ == null) {\r
309                 throw new NullPointerException(\r
310                             "The field DataManipulate in this Trie is null");\r
311             }\r
312             // enumerate trail surrogates for this lead surrogate\r
313             m_nextIndex_ = m_trie_.m_dataManipulate_.getFoldingOffset(\r
314                                m_trie_.getValue(leadBlock + \r
315                                    (nextLead & Trie.INDEX_STAGE_3_MASK_)));\r
316             if (m_nextIndex_ <= 0) {\r
317                 // no data for this lead surrogate\r
318                 if (currentValue != m_initialValue_) {\r
319                     m_nextValue_      = m_initialValue_;\r
320                     m_nextBlock_      = m_trie_.m_dataOffset_;\r
321                     m_nextBlockIndex_ = 0;\r
322                     setResult(element, m_currentCodepoint_, m_nextCodepoint_, \r
323                               currentValue);\r
324                     m_currentCodepoint_ = m_nextCodepoint_;\r
325                     return;\r
326                 }\r
327                 m_nextCodepoint_ += TRAIL_SURROGATE_COUNT_;\r
328             } else {\r
329                 m_nextTrailIndexOffset_ = 0;\r
330                 if (!checkTrailBlock(currentValue)) {\r
331                     setResult(element, m_currentCodepoint_, m_nextCodepoint_, \r
332                               currentValue);\r
333                     m_currentCodepoint_ = m_nextCodepoint_;\r
334                     return;\r
335                 }\r
336             }    \r
337             nextLead ++;\r
338          }\r
339 \r
340          // deliver last range\r
341          setResult(element, m_currentCodepoint_, UCharacter.MAX_VALUE + 1, \r
342                    currentValue);\r
343     }    \r
344     \r
345     /**\r
346     * Internal block value calculations\r
347     * Performs calculations on a data block to find codepoints in m_nextBlock_\r
348     * after the index m_nextBlockIndex_ that has the same value.\r
349     * Note m_*_ variables at this point is the next codepoint whose value\r
350     * has not been calculated.\r
351     * But when returned with false, it will be the last codepoint whose\r
352     * value has been calculated.\r
353     * @param currentValue the value which other codepoints are tested against\r
354     * @return true if the whole block has the same value as currentValue or if\r
355     *              the whole block has been calculated, false otherwise.\r
356     */\r
357     private final boolean checkBlockDetail(int currentValue)\r
358     {\r
359         while (m_nextBlockIndex_ < DATA_BLOCK_LENGTH_) {\r
360             m_nextValue_ = extract(m_trie_.getValue(m_nextBlock_ + \r
361                                                     m_nextBlockIndex_));\r
362             if (m_nextValue_ != currentValue) {\r
363                 return false;\r
364             }\r
365             ++ m_nextBlockIndex_;\r
366             ++ m_nextCodepoint_;\r
367         }\r
368         return true;\r
369     }\r
370     \r
371     /**\r
372     * Internal block value calculations\r
373     * Performs calculations on a data block to find codepoints in m_nextBlock_\r
374     * that has the same value. \r
375     * Will call checkBlockDetail() if highlevel check fails.\r
376     * Note m_*_ variables at this point is the next codepoint whose value\r
377     * has not been calculated.\r
378     * @param currentBlock the initial block containing all currentValue\r
379     * @param currentValue the value which other codepoints are tested against\r
380     * @return true if the whole block has the same value as currentValue or if\r
381     *              the whole block has been calculated, false otherwise.\r
382     */\r
383     private final boolean checkBlock(int currentValue) \r
384     {\r
385         int currentBlock = m_nextBlock_;\r
386         m_nextBlock_ = m_trie_.m_index_[m_nextIndex_] << \r
387                                                   Trie.INDEX_STAGE_2_SHIFT_;\r
388         if (m_nextBlock_ == currentBlock &&\r
389             (m_nextCodepoint_ - m_currentCodepoint_) >= DATA_BLOCK_LENGTH_) {\r
390             // the block is the same as the previous one, filled with \r
391             // currentValue\r
392             m_nextCodepoint_ += DATA_BLOCK_LENGTH_;\r
393         }\r
394         else if (m_nextBlock_ == m_trie_.m_dataOffset_) {\r
395             // this is the all-initial-value block\r
396             if (currentValue != m_initialValue_) {\r
397                 m_nextValue_      = m_initialValue_;\r
398                 m_nextBlockIndex_ = 0;\r
399                 return false;\r
400             }\r
401             m_nextCodepoint_ += DATA_BLOCK_LENGTH_;\r
402         }\r
403         else {\r
404             if (!checkBlockDetail(currentValue)) {\r
405                 return false;\r
406             }\r
407         }\r
408         return true;\r
409     }\r
410     \r
411     /**\r
412     * Internal block value calculations\r
413     * Performs calculations on multiple data blocks for a set of trail \r
414     * surrogates to find codepoints in m_nextBlock_ that has the same value. \r
415     * Will call checkBlock() for internal block checks.\r
416     * Note m_*_ variables at this point is the next codepoint whose value\r
417     * has not been calculated.\r
418     * @param currentValue the value which other codepoints are tested against\r
419     * @return true if the whole block has the same value as currentValue or if\r
420     *              the whole block has been calculated, false otherwise.\r
421     */\r
422     private final boolean checkTrailBlock(int currentValue)\r
423     {\r
424         // enumerate code points for this lead surrogate\r
425         while (m_nextTrailIndexOffset_ < TRAIL_SURROGATE_INDEX_BLOCK_LENGTH_) \r
426         {\r
427             // if we ever reach here, we are at the start of a new block\r
428             m_nextBlockIndex_ = 0;\r
429             // copy of most of the body of the BMP loop\r
430             if (!checkBlock(currentValue)) {\r
431                 return false;\r
432             }\r
433             m_nextTrailIndexOffset_ ++;\r
434             m_nextIndex_ ++;\r
435         }\r
436         return true;\r
437     }\r
438     \r
439     /**\r
440     * Checks if we are beginning at the start of a initial block.\r
441     * If we are then the rest of the codepoints in this initial block\r
442     * has the same values.\r
443     * We increment m_nextCodepoint_ and relevant data members if so.\r
444     * This is used only in for the supplementary codepoints because\r
445     * the offset to the trail indexes could be 0.\r
446     * @return true if we are at the start of a initial block.\r
447     */\r
448     private final boolean checkNullNextTrailIndex()\r
449     {\r
450         if (m_nextIndex_ <= 0) {\r
451             m_nextCodepoint_ += TRAIL_SURROGATE_COUNT_ - 1;\r
452             int nextLead  = UTF16.getLeadSurrogate(m_nextCodepoint_);\r
453             int leadBlock = \r
454                    m_trie_.m_index_[nextLead >> Trie.INDEX_STAGE_1_SHIFT_] << \r
455                                                    Trie.INDEX_STAGE_2_SHIFT_;\r
456             if (m_trie_.m_dataManipulate_ == null) {\r
457                 throw new NullPointerException(\r
458                             "The field DataManipulate in this Trie is null");\r
459             }\r
460             m_nextIndex_ = m_trie_.m_dataManipulate_.getFoldingOffset(\r
461                                m_trie_.getValue(leadBlock + \r
462                                    (nextLead & Trie.INDEX_STAGE_3_MASK_)));\r
463             m_nextIndex_ --;\r
464             m_nextBlockIndex_ =  DATA_BLOCK_LENGTH_;\r
465             return true;\r
466         }\r
467         return false;\r
468     }\r
469 \r
470     // private data members --------------------------------------------\r
471 \r
472     /**\r
473     * Size of the stage 1 BMP indexes\r
474     */\r
475     private static final int BMP_INDEX_LENGTH_ =\r
476                                         0x10000 >> Trie.INDEX_STAGE_1_SHIFT_;\r
477     /**\r
478     * Lead surrogate minimum value\r
479     */\r
480     private static final int LEAD_SURROGATE_MIN_VALUE_ = 0xD800;\r
481     /**\r
482     * Trail surrogate minimum value\r
483     */\r
484     private static final int TRAIL_SURROGATE_MIN_VALUE_ = 0xDC00;\r
485     /*\r
486     * Trail surrogate maximum value\r
487     */\r
488     //private static final int TRAIL_SURROGATE_MAX_VALUE_ = 0xDFFF;\r
489     /**\r
490     * Number of trail surrogate\r
491     */\r
492     private static final int TRAIL_SURROGATE_COUNT_ = 0x400;\r
493     /**\r
494     * Number of stage 1 indexes for supplementary calculations that maps to\r
495     * each lead surrogate character.\r
496     * See second pass into getRawOffset for the trail surrogate character.\r
497     * 10 for significant number of bits for trail surrogates, 5 for what we\r
498     * discard during shifting.\r
499     */\r
500     private static final int TRAIL_SURROGATE_INDEX_BLOCK_LENGTH_ =\r
501                                     1 << (10 - Trie.INDEX_STAGE_1_SHIFT_);\r
502     /**\r
503     * Number of data values in a stage 2 (data array) block.\r
504     */\r
505     private static final int DATA_BLOCK_LENGTH_ = \r
506                                               1 << Trie.INDEX_STAGE_1_SHIFT_;\r
507 //    /**\r
508 //    * Number of codepoints in a stage 2 block\r
509 //    */\r
510 //    private static final int DATA_BLOCK_SUPPLEMENTARY_LENGTH_ =\r
511 //                                                     DATA_BLOCK_LENGTH_ << 10;\r
512     /**\r
513     * Trie instance\r
514     */\r
515     private Trie m_trie_;\r
516     /**\r
517     * Initial value for trie values\r
518     */\r
519     private int m_initialValue_;\r
520     /**\r
521     * Next element results and data.\r
522     */\r
523     private int m_currentCodepoint_;\r
524     private int m_nextCodepoint_;\r
525     private int m_nextValue_;\r
526     private int m_nextIndex_;\r
527     private int m_nextBlock_;\r
528     private int m_nextBlockIndex_;\r
529     private int m_nextTrailIndexOffset_;\r
530 }\r