]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/text/CollationElementIterator.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / text / CollationElementIterator.java
1 /**\r
2 *******************************************************************************\r
3 * Copyright (C) 1996-2008, International Business Machines Corporation and    *\r
4 * others. All Rights Reserved.                                                *\r
5 *******************************************************************************\r
6 *\r
7 *\r
8 *******************************************************************************\r
9 */\r
10 package com.ibm.icu.text;\r
11 \r
12 /***\r
13  * import java.text.StringCharacterIterator;\r
14  * import java.text.CharacterIterator;\r
15  */\r
16 import com.ibm.icu.impl.NormalizerImpl;\r
17 import com.ibm.icu.impl.UCharacterProperty;\r
18 import com.ibm.icu.impl.StringUCharacterIterator;\r
19 import com.ibm.icu.impl.CharacterIteratorWrapper;\r
20 import com.ibm.icu.impl.ICUDebug;\r
21 import com.ibm.icu.lang.UCharacter;\r
22 import java.text.CharacterIterator;\r
23 import java.util.MissingResourceException;\r
24 \r
25 /**\r
26  * <p><code>CollationElementIterator</code> is an iterator created by\r
27  * a RuleBasedCollator to walk through a string. The return result of\r
28  * each iteration is a 32-bit collation element that defines the\r
29  * ordering priority of the next character or sequence of characters\r
30  * in the source string.</p>\r
31  *\r
32  * <p>For illustration, consider the following in Spanish:\r
33  * <blockquote>\r
34  * <pre>\r
35  * "ca" -> the first collation element is collation_element('c') and second\r
36  *         collation element is collation_element('a').\r
37  *\r
38  * Since "ch" in Spanish sorts as one entity, the below example returns one\r
39  * collation element for the two characters 'c' and 'h'\r
40  *\r
41  * "cha" -> the first collation element is collation_element('ch') and second\r
42  *          collation element is collation_element('a').\r
43  * </pre>\r
44  * </blockquote>\r
45  * And in German,\r
46  * <blockquote>\r
47  * <pre>\r
48  * Since the character '&#230;' is a composed character of 'a' and 'e', the\r
49  * iterator returns two collation elements for the single character '&#230;'\r
50  *\r
51  * "&#230;b" -> the first collation element is collation_element('a'), the\r
52  *              second collation element is collation_element('e'), and the\r
53  *              third collation element is collation_element('b').\r
54  * </pre>\r
55  * </blockquote>\r
56  * </p>\r
57  *\r
58  * <p>For collation ordering comparison, the collation element results\r
59  * can not be compared simply by using basic arithmetric operators,\r
60  * e.g. &lt;, == or &gt;, further processing has to be done. Details\r
61  * can be found in the ICU\r
62  * <a href="http://www.icu-project.org/userguide/Collate_ServiceArchitecture.html">\r
63  * user guide</a>. An example of using the CollationElementIterator\r
64  * for collation ordering comparison is the class\r
65  * <a href=StringSearch.html> com.ibm.icu.text.StringSearch</a>.</p>\r
66  *\r
67  * <p>To construct a CollationElementIterator object, users\r
68  * call the method getCollationElementIterator() on a\r
69  * RuleBasedCollator that defines the desired sorting order.</p>\r
70  *\r
71  * <p> Example:\r
72  * <blockquote>\r
73  * <pre>\r
74  *  String testString = "This is a test";\r
75  *  RuleBasedCollator rbc = new RuleBasedCollator("&amp;a&lt;b");\r
76  *  CollationElementIterator iterator = rbc.getCollationElementIterator(testString);\r
77  *  int primaryOrder = iterator.IGNORABLE;\r
78  *  while (primaryOrder != iterator.NULLORDER) {\r
79  *      int order = iterator.next();\r
80  *      if (order != iterator.IGNORABLE &&\r
81  *          order != iterator.NULLORDER) {\r
82  *          // order is valid, not ignorable and we have not passed the end\r
83  *          // of the iteration, we do something\r
84  *          primaryOrder = CollationElementIterator.primaryOrder(order);\r
85  *          System.out.println("Next primary order 0x" +\r
86  *                             Integer.toHexString(primaryOrder));\r
87  *      }\r
88  *  }\r
89  * </pre>\r
90  * </blockquote>\r
91  * </p>\r
92  * <p>\r
93  * This class is not subclassable\r
94  * </p>\r
95  * @see Collator\r
96  * @see RuleBasedCollator\r
97  * @see StringSearch\r
98  * @author Syn Wee Quek\r
99  * @stable ICU 2.8\r
100  */\r
101 public final class CollationElementIterator\r
102 {\r
103   \r
104     \r
105     // public data members --------------------------------------------------\r
106 \r
107     /**\r
108      * <p>This constant is returned by the iterator in the methods\r
109      * next() and previous() when the end or the beginning of the\r
110      * source string has been reached, and there are no more valid\r
111      * collation elements to return.</p>\r
112      *\r
113      * <p>See class documentation for an example of use.</p>\r
114      * @stable ICU 2.8\r
115      * @see #next\r
116      * @see #previous */\r
117     public final static int NULLORDER = 0xffffffff;\r
118 \r
119     /**\r
120      * <p>This constant is returned by the iterator in the methods\r
121      * next() and previous() when a collation element result is to be\r
122      * ignored.</p>\r
123      *\r
124      * <p>See class documentation for an example of use.</p>\r
125      * @stable ICU 2.8\r
126      * @see #next\r
127      * @see #previous */\r
128     public static final int IGNORABLE = 0;\r
129 \r
130     // public methods -------------------------------------------------------\r
131 \r
132     // public getters -------------------------------------------------------\r
133 \r
134     /**\r
135      * <p>Returns the character offset in the source string\r
136      * corresponding to the next collation element. I.e., getOffset()\r
137      * returns the position in the source string corresponding to the\r
138      * collation element that will be returned by the next call to\r
139      * next(). This value could be any of:\r
140      * <ul>\r
141      * <li> The index of the <b>first</b> character corresponding to\r
142      * the next collation element. (This means that if\r
143      * <code>setOffset(offset)</code> sets the index in the middle of\r
144      * a contraction, <code>getOffset()</code> returns the index of\r
145      * the first character in the contraction, which may not be equal\r
146      * to the original offset that was set. Hence calling getOffset()\r
147      * immediately after setOffset(offset) does not guarantee that the\r
148      * original offset set will be returned.)\r
149      * <li> If normalization is on, the index of the <b>immediate</b>\r
150      * subsequent character, or composite character with the first\r
151      * character, having a combining class of 0.\r
152      * <li> The length of the source string, if iteration has reached\r
153      * the end.\r
154      *</ul>\r
155      * </p>\r
156      * @return The character offset in the source string corresponding to the\r
157      *         collation element that will be returned by the next call to\r
158      *         next().\r
159      * @stable ICU 2.8\r
160      */\r
161     public int getOffset()\r
162     {\r
163         if (m_bufferOffset_ != -1) {\r
164             if (m_isForwards_) {\r
165                 return m_FCDLimit_;\r
166             }\r
167             return m_FCDStart_;\r
168         }\r
169         return m_source_.getIndex();\r
170     }\r
171 \r
172 \r
173     /**\r
174      * <p> Returns the maximum length of any expansion sequence that ends with\r
175      * the specified collation element. If there is no expansion with this\r
176      * collation element as the last element, returns 1.\r
177      * </p>\r
178      * @param ce a collation element returned by previous() or next().\r
179      * @return the maximum length of any expansion sequence ending\r
180      *         with the specified collation element.\r
181      * @stable ICU 2.8\r
182      */\r
183     public int getMaxExpansion(int ce)\r
184     {\r
185         int start = 0;\r
186         int limit = m_collator_.m_expansionEndCE_.length;\r
187         long unsignedce = ce & 0xFFFFFFFFl;\r
188         while (start < limit - 1) {\r
189             int mid = start + ((limit - start) >> 1);\r
190             long midce = m_collator_.m_expansionEndCE_[mid] & 0xFFFFFFFFl;\r
191             if (unsignedce <= midce) {\r
192                 limit = mid;\r
193             }\r
194             else {\r
195                 start = mid;\r
196             }\r
197         }\r
198         int result = 1;\r
199         if (m_collator_.m_expansionEndCE_[start] == ce) {\r
200             result = m_collator_.m_expansionEndCEMaxSize_[start];\r
201         }\r
202         else if (limit < m_collator_.m_expansionEndCE_.length &&\r
203                  m_collator_.m_expansionEndCE_[limit] == ce) {\r
204             result = m_collator_.m_expansionEndCEMaxSize_[limit];\r
205         }\r
206         else if ((ce & 0xFFFF) == 0x00C0) {\r
207             result = 2;\r
208         }\r
209         return result;\r
210     }\r
211 \r
212     // public other methods -------------------------------------------------\r
213 \r
214     /**\r
215      * <p> Resets the cursor to the beginning of the string. The next\r
216      * call to next() or previous() will return the first and last\r
217      * collation element in the string, respectively.</p>\r
218      *\r
219      * <p>If the RuleBasedCollator used by this iterator has had its\r
220      * attributes changed, calling reset() will reinitialize the\r
221      * iterator to use the new attributes.</p>\r
222      *\r
223      * @stable ICU 2.8\r
224      */\r
225     public void reset()\r
226     {\r
227         m_source_.setToStart();\r
228         updateInternalState();\r
229     }\r
230 \r
231     /**\r
232      * <p>Get the next collation element in the source string.</p>\r
233      *\r
234      * <p>This iterator iterates over a sequence of collation elements\r
235      * that were built from the string. Because there isn't\r
236      * necessarily a one-to-one mapping from characters to collation\r
237      * elements, this doesn't mean the same thing as "return the\r
238      * collation element [or ordering priority] of the next character\r
239      * in the string".</p>\r
240      *\r
241      * <p>This function returns the collation element that the\r
242      * iterator is currently pointing to, and then updates the\r
243      * internal pointer to point to the next element.  Previous()\r
244      * updates the pointer first, and then returns the element. This\r
245      * means that when you change direction while iterating (i.e.,\r
246      * call next() and then call previous(), or call previous() and\r
247      * then call next()), you'll get back the same element twice.</p>\r
248      *\r
249      * @return the next collation element or NULLORDER if the end of the\r
250      *         iteration has been reached.\r
251      * @stable ICU 2.8\r
252      */\r
253     public int next()\r
254     {\r
255         m_isForwards_ = true;\r
256         if (m_CEBufferSize_ > 0) {\r
257             if (m_CEBufferOffset_ < m_CEBufferSize_) {\r
258                 // if there are expansions left in the buffer, we return it\r
259                 return m_CEBuffer_[m_CEBufferOffset_ ++];\r
260             }\r
261             m_CEBufferSize_ = 0;\r
262             m_CEBufferOffset_ = 0;\r
263         }\r
264  \r
265         int ch_int = nextChar();\r
266         \r
267         if (ch_int == UCharacterIterator.DONE) {\r
268             return NULLORDER;\r
269         }\r
270         char ch = (char)ch_int;\r
271         if (m_collator_.m_isHiragana4_) {\r
272             /* Codepoints \u3099-\u309C are both Hiragana and Katakana. Set the flag\r
273              * based on whether the previous codepoint was Hiragana or Katakana.\r
274              */\r
275             m_isCodePointHiragana_ = (m_isCodePointHiragana_ && (ch >= 0x3099 && ch <= 0x309C)) || \r
276                                      ((ch >= 0x3040 && ch <= 0x309e) && !(ch > 0x3094 && ch < 0x309d));\r
277         }\r
278 \r
279         int result = NULLORDER;\r
280         if (ch <= 0xFF) {\r
281             // For latin-1 characters we never need to fall back to the UCA\r
282             // table because all of the UCA data is replicated in the\r
283             // latinOneMapping array\r
284             result = m_collator_.m_trie_.getLatin1LinearValue(ch);\r
285             if (RuleBasedCollator.isSpecial(result)) {\r
286                 result = nextSpecial(m_collator_, result, ch);\r
287             }\r
288         }\r
289         else {\r
290             result = m_collator_.m_trie_.getLeadValue(ch);\r
291             //System.out.println(Integer.toHexString(result));\r
292             if (RuleBasedCollator.isSpecial(result)) {\r
293                 // surrogate leads are handled as special ces\r
294                 result = nextSpecial(m_collator_, result, ch);\r
295             }\r
296             if (result == CE_NOT_FOUND_ && RuleBasedCollator.UCA_ != null) {\r
297                 // couldn't find a good CE in the tailoring\r
298                 // if we got here, the codepoint MUST be over 0xFF - so we look\r
299                 // directly in the UCA\r
300                 result = RuleBasedCollator.UCA_.m_trie_.getLeadValue(ch);\r
301                 if (RuleBasedCollator.isSpecial(result)) {\r
302                     // UCA also gives us a special CE\r
303                     result = nextSpecial(RuleBasedCollator.UCA_, result, ch);\r
304                 }\r
305             }\r
306         }\r
307         if(result == CE_NOT_FOUND_) { \r
308             // maybe there is no UCA, unlikely in Java, but ported for consistency\r
309             result = nextImplicit(ch); \r
310         }\r
311         return result;\r
312     }\r
313 \r
314     /**\r
315      * <p>Get the previous collation element in the source string.</p>\r
316      *\r
317      * <p>This iterator iterates over a sequence of collation elements\r
318      * that were built from the string. Because there isn't\r
319      * necessarily a one-to-one mapping from characters to collation\r
320      * elements, this doesn't mean the same thing as "return the\r
321      * collation element [or ordering priority] of the previous\r
322      * character in the string".</p>\r
323      *\r
324      * <p>This function updates the iterator's internal pointer to\r
325      * point to the collation element preceding the one it's currently\r
326      * pointing to and then returns that element, while next() returns\r
327      * the current element and then updates the pointer. This means\r
328      * that when you change direction while iterating (i.e., call\r
329      * next() and then call previous(), or call previous() and then\r
330      * call next()), you'll get back the same element twice.</p>\r
331      *\r
332      * @return the previous collation element, or NULLORDER when the start of\r
333      *             the iteration has been reached.\r
334      * @stable ICU 2.8\r
335      */\r
336     public int previous()\r
337     {\r
338         if (m_source_.getIndex() <= 0 && m_isForwards_) {\r
339             // if iterator is new or reset, we can immediate perform  backwards\r
340             // iteration even when the offset is not right.\r
341             m_source_.setToLimit();\r
342             updateInternalState();\r
343         }\r
344         m_isForwards_ = false;\r
345         int result = NULLORDER;\r
346         if (m_CEBufferSize_ > 0) {\r
347             if (m_CEBufferOffset_ > 0) {\r
348                 return m_CEBuffer_[-- m_CEBufferOffset_];\r
349             }\r
350             m_CEBufferSize_ = 0;\r
351             m_CEBufferOffset_ = 0;\r
352         }\r
353         int ch_int = previousChar();\r
354         if (ch_int == UCharacterIterator.DONE) {\r
355             return NULLORDER;\r
356         }\r
357         char ch = (char)ch_int;\r
358         if (m_collator_.m_isHiragana4_) {\r
359             m_isCodePointHiragana_ = (ch >= 0x3040 && ch <= 0x309f);\r
360         }\r
361         if (m_collator_.isContractionEnd(ch) && !isBackwardsStart()) {\r
362             result = previousSpecial(m_collator_, CE_CONTRACTION_, ch);\r
363         }\r
364         else {\r
365             if (ch <= 0xFF) {\r
366                 result = m_collator_.m_trie_.getLatin1LinearValue(ch);\r
367             }\r
368             else {\r
369                 result = m_collator_.m_trie_.getLeadValue(ch);\r
370             }\r
371             if (RuleBasedCollator.isSpecial(result)) {\r
372                 result = previousSpecial(m_collator_, result, ch);\r
373             }\r
374             if (result == CE_NOT_FOUND_) {\r
375                 if (!isBackwardsStart()\r
376                     && m_collator_.isContractionEnd(ch)) {\r
377                     result = CE_CONTRACTION_;\r
378                 }\r
379                 else {\r
380                     if(RuleBasedCollator.UCA_ != null) {\r
381                         result = RuleBasedCollator.UCA_.m_trie_.getLeadValue(ch);\r
382                     }\r
383                 }\r
384 \r
385                 if (RuleBasedCollator.isSpecial(result)) {\r
386                     if(RuleBasedCollator.UCA_ != null) {                    \r
387                         result = previousSpecial(RuleBasedCollator.UCA_, result, ch);\r
388                     }\r
389                 }\r
390             }\r
391         }\r
392         if(result == CE_NOT_FOUND_) {\r
393             result = previousImplicit(ch);\r
394         }\r
395         return result;\r
396     }\r
397 \r
398     /**\r
399      * Return the primary order of the specified collation element,\r
400      * i.e. the first 16 bits.  This value is unsigned.\r
401      * @param ce the collation element\r
402      * @return the element's 16 bits primary order.\r
403      * @stable ICU 2.8\r
404      */\r
405     public final static int primaryOrder(int ce)\r
406     {\r
407         return (ce & RuleBasedCollator.CE_PRIMARY_MASK_)\r
408             >>> RuleBasedCollator.CE_PRIMARY_SHIFT_;\r
409     }\r
410     /**\r
411      * Return the secondary order of the specified collation element,\r
412      * i.e. the 16th to 23th bits, inclusive.  This value is unsigned.\r
413      * @param ce the collation element\r
414      * @return the element's 8 bits secondary order\r
415      * @stable ICU 2.8\r
416      */\r
417     public final static int secondaryOrder(int ce)\r
418     {\r
419         return (ce & RuleBasedCollator.CE_SECONDARY_MASK_)\r
420             >> RuleBasedCollator.CE_SECONDARY_SHIFT_;\r
421     }\r
422 \r
423     /**\r
424      * Return the tertiary order of the specified collation element, i.e. the last\r
425      * 8 bits.  This value is unsigned.\r
426      * @param ce the collation element\r
427      * @return the element's 8 bits tertiary order\r
428      * @stable ICU 2.8\r
429      */\r
430     public final static int tertiaryOrder(int ce)\r
431     {\r
432         return ce & RuleBasedCollator.CE_TERTIARY_MASK_;\r
433     }\r
434 \r
435     /**\r
436      * <p> Sets the iterator to point to the collation element\r
437      * corresponding to the character at the specified offset. The\r
438      * value returned by the next call to next() will be the collation\r
439      * element corresponding to the characters at offset.</p>\r
440      *\r
441      * <p>If offset is in the middle of a contracting character\r
442      * sequence, the iterator is adjusted to the start of the\r
443      * contracting sequence. This means that getOffset() is not\r
444      * guaranteed to return the same value set by this method.</p>\r
445      *\r
446      * <p>If the decomposition mode is on, and offset is in the middle\r
447      * of a decomposible range of source text, the iterator may not\r
448      * return a correct result for the next forwards or backwards\r
449      * iteration.  The user must ensure that the offset is not in the\r
450      * middle of a decomposible range.</p>\r
451      *\r
452      * @param offset the character offset into the original source string to\r
453      *        set. Note that this is not an offset into the corresponding\r
454      *        sequence of collation elements.\r
455      * @stable ICU 2.8\r
456      */\r
457     public void setOffset(int offset)\r
458     {\r
459         m_source_.setIndex(offset);\r
460         int ch_int = m_source_.current();\r
461         char ch = (char)ch_int;\r
462         if (ch_int != UCharacterIterator.DONE && m_collator_.isUnsafe(ch)) {\r
463             // if it is unsafe we need to check if it is part of a contraction\r
464             // or a surrogate character\r
465             if (UTF16.isTrailSurrogate(ch)) {\r
466                 // if it is a surrogate pair we move up one character\r
467                 char prevch = (char)m_source_.previous();\r
468                 if (!UTF16.isLeadSurrogate(prevch)) {\r
469                     m_source_.setIndex(offset); // go back to the same index\r
470                 }\r
471             }\r
472             else {\r
473                 // could be part of a contraction\r
474                 // backup to a safe point and iterate till we pass offset\r
475                 while (m_source_.getIndex() > 0) {\r
476                     if (!m_collator_.isUnsafe(ch)) {\r
477                         break;\r
478                     }\r
479                     ch = (char)m_source_.previous();\r
480                 }\r
481                 updateInternalState();\r
482                 int prevoffset = 0;\r
483                 while (m_source_.getIndex() <= offset) {\r
484                     prevoffset = m_source_.getIndex();\r
485                     next();\r
486                 }\r
487                 m_source_.setIndex(prevoffset);\r
488             }\r
489         }\r
490         updateInternalState();\r
491         // direction code to prevent next and previous from returning a \r
492         // character if we are already at the ends\r
493         offset = m_source_.getIndex();\r
494         if (offset == 0/* m_source_.getBeginIndex() */) {\r
495             // preventing previous() from returning characters from the end of \r
496             // the string again if we are at the beginning\r
497             m_isForwards_ = false; \r
498         }\r
499         else if (offset == m_source_.getLength()) {\r
500             // preventing next() from returning characters from the start of \r
501             // the string again if we are at the end\r
502             m_isForwards_ = true;\r
503         }\r
504     }\r
505 \r
506     /**\r
507      * <p>Set a new source string for iteration, and reset the offset\r
508      * to the beginning of the text.</p>\r
509      *\r
510      * @param source the new source string for iteration.\r
511      * @stable ICU 2.8\r
512      */\r
513     public void setText(String source)\r
514     {\r
515         m_srcUtilIter_.setText(source);\r
516         m_source_ = m_srcUtilIter_;\r
517         updateInternalState();\r
518     }\r
519     \r
520     /**\r
521      * <p>Set a new source string iterator for iteration, and reset the\r
522      * offset to the beginning of the text.\r
523      * </p>\r
524      * <p>The source iterator's integrity will be preserved since a new copy\r
525      * will be created for use.</p>\r
526      * @param source the new source string iterator for iteration.\r
527      * @stable ICU 2.8\r
528      */\r
529     public void setText(UCharacterIterator source)\r
530     {\r
531         m_srcUtilIter_.setText(source.getText());\r
532         m_source_ = m_srcUtilIter_;\r
533         updateInternalState(); \r
534     }\r
535 \r
536     /**\r
537      * <p>Set a new source string iterator for iteration, and reset the\r
538      * offset to the beginning of the text.\r
539      * </p>\r
540      * @param source the new source string iterator for iteration.\r
541      * @stable ICU 2.8\r
542      */\r
543     public void setText(CharacterIterator source)\r
544     {\r
545         m_source_ = new CharacterIteratorWrapper(source);\r
546         m_source_.setToStart();\r
547         updateInternalState();\r
548     }\r
549 \r
550     // public miscellaneous methods -----------------------------------------\r
551 \r
552     /**\r
553      * Tests that argument object is equals to this CollationElementIterator.\r
554      * Iterators are equal if the objects uses the same RuleBasedCollator,\r
555      * the same source text and have the same current position in iteration.\r
556      * @param that object to test if it is equals to this\r
557      *             CollationElementIterator\r
558      * @stable ICU 2.8\r
559      */\r
560     public boolean equals(Object that)\r
561     {\r
562         if (that == this) {\r
563             return true;\r
564         }\r
565         if (that instanceof CollationElementIterator) {\r
566             CollationElementIterator thatceiter\r
567                                               = (CollationElementIterator)that;\r
568             if (!m_collator_.equals(thatceiter.m_collator_)) {\r
569                 return false;\r
570             }\r
571             // checks the text \r
572             return m_source_.getIndex() == thatceiter.m_source_.getIndex()\r
573                    && m_source_.getText().equals(\r
574                                             thatceiter.m_source_.getText());\r
575         }\r
576         return false;\r
577     }\r
578 \r
579     // package private constructors ------------------------------------------\r
580 \r
581     /**\r
582      * <p>CollationElementIterator constructor. This takes a source\r
583      * string and a RuleBasedCollator. The iterator will walk through\r
584      * the source string based on the rules defined by the\r
585      * collator. If the source string is empty, NULLORDER will be\r
586      * returned on the first call to next().</p>\r
587      *\r
588      * @param source the source string.\r
589      * @param collator the RuleBasedCollator\r
590      * @stable ICU 2.8\r
591      */\r
592     CollationElementIterator(String source, RuleBasedCollator collator)\r
593     {\r
594         m_srcUtilIter_ = new StringUCharacterIterator(source);\r
595         m_utilStringBuffer_ = new StringBuffer();\r
596         m_source_ = m_srcUtilIter_;\r
597         m_collator_ = collator;\r
598         m_CEBuffer_ = new int[CE_BUFFER_INIT_SIZE_];\r
599         m_buffer_ = new StringBuffer();\r
600         m_utilSpecialBackUp_ = new Backup();\r
601         updateInternalState();\r
602     }\r
603 \r
604     /**\r
605      * <p>CollationElementIterator constructor. This takes a source\r
606      * character iterator and a RuleBasedCollator. The iterator will\r
607      * walk through the source string based on the rules defined by\r
608      * the collator. If the source string is empty, NULLORDER will be\r
609      * returned on the first call to next().</p>\r
610      *\r
611      * @param source the source string iterator.\r
612      * @param collator the RuleBasedCollator\r
613      * @stable ICU 2.8\r
614      */\r
615     CollationElementIterator(CharacterIterator source,\r
616                              RuleBasedCollator collator)\r
617     {\r
618         m_srcUtilIter_ = new StringUCharacterIterator();\r
619         m_utilStringBuffer_ = new StringBuffer();\r
620         m_source_ = new CharacterIteratorWrapper(source);\r
621         m_collator_ = collator;\r
622         m_CEBuffer_ = new int[CE_BUFFER_INIT_SIZE_];\r
623         m_buffer_ = new StringBuffer();\r
624         m_utilSpecialBackUp_ = new Backup();\r
625         updateInternalState();\r
626     }\r
627     \r
628     /**\r
629      * <p>CollationElementIterator constructor. This takes a source\r
630      * character iterator and a RuleBasedCollator. The iterator will\r
631      * walk through the source string based on the rules defined by\r
632      * the collator. If the source string is empty, NULLORDER will be\r
633      * returned on the first call to next().</p>\r
634      *\r
635      * @param source the source string iterator.\r
636      * @param collator the RuleBasedCollator\r
637      * @stable ICU 2.8\r
638      */\r
639     CollationElementIterator(UCharacterIterator source,\r
640                              RuleBasedCollator collator)\r
641     {\r
642         m_srcUtilIter_ = new StringUCharacterIterator();\r
643         m_utilStringBuffer_ = new StringBuffer();\r
644         m_srcUtilIter_.setText(source.getText());\r
645         m_source_ = m_srcUtilIter_;\r
646         m_collator_ = collator;\r
647         m_CEBuffer_ = new int[CE_BUFFER_INIT_SIZE_];\r
648         m_buffer_ = new StringBuffer();\r
649         m_utilSpecialBackUp_ = new Backup();\r
650         updateInternalState();\r
651     }\r
652 \r
653     // package private data members -----------------------------------------\r
654 \r
655     /**\r
656      * true if current codepoint was Hiragana\r
657      */\r
658     boolean m_isCodePointHiragana_;\r
659     /**\r
660      * Position in the original string that starts with a non-FCD sequence\r
661      */\r
662     int m_FCDStart_;\r
663     /**\r
664      * This is the CE from CEs buffer that should be returned.\r
665      * Initial value is 0.\r
666      * Forwards iteration will end with m_CEBufferOffset_ == m_CEBufferSize_,\r
667      * backwards will end with m_CEBufferOffset_ == 0.\r
668      * The next/previous after we reach the end/beginning of the m_CEBuffer_\r
669      * will cause this value to be reset to 0.\r
670      */\r
671     int m_CEBufferOffset_;\r
672 \r
673     /**\r
674      * This is the position to which we have stored processed CEs.\r
675      * Initial value is 0.\r
676      * The next/previous after we reach the end/beginning of the m_CEBuffer_\r
677      * will cause this value to be reset to 0.\r
678      */\r
679     int m_CEBufferSize_;\r
680     static final int CE_NOT_FOUND_ = 0xF0000000;\r
681     static final int CE_EXPANSION_TAG_ = 1;\r
682     static final int CE_CONTRACTION_TAG_ = 2;\r
683     /** \r
684      * Collate Digits As Numbers (CODAN) implementation\r
685      */\r
686     static final int CE_DIGIT_TAG_ = 13;\r
687 \r
688     // package private methods ----------------------------------------------\r
689 \r
690     /**\r
691      * Sets the collator used.\r
692      * Internal use, all data members will be reset to the default values\r
693      * @param collator to set\r
694      */\r
695     void setCollator(RuleBasedCollator collator)\r
696     {\r
697         m_collator_ = collator;\r
698         updateInternalState();\r
699     }\r
700 \r
701     /**\r
702      * <p>Sets the iterator to point to the collation element corresponding to\r
703      * the specified character (the parameter is a CHARACTER offset in the\r
704      * original string, not an offset into its corresponding sequence of\r
705      * collation elements). The value returned by the next call to next()\r
706      * will be the collation element corresponding to the specified position\r
707      * in the text. Unlike the public method setOffset(int), this method does\r
708      * not try to readjust the offset to the start of a contracting sequence.\r
709      * getOffset() is guaranteed to return the same value as was passed to a\r
710      * preceding call to setOffset().</p>\r
711      * @param offset new character offset into the original text to set.\r
712      */\r
713     void setExactOffset(int offset)\r
714     {\r
715         m_source_.setIndex(offset);\r
716         updateInternalState();\r
717     }\r
718 \r
719     /**\r
720      * Checks if iterator is in the buffer zone\r
721      * @return true if iterator is in buffer zone, false otherwise\r
722      */\r
723     boolean isInBuffer()\r
724     {\r
725         return m_bufferOffset_ > 0;\r
726     }\r
727 \r
728    \r
729     /**\r
730      * <p>Sets the iterator to point to the collation element corresponding to\r
731      * the specified character (the parameter is a CHARACTER offset in the\r
732      * original string, not an offset into its corresponding sequence of\r
733      * collation elements). The value returned by the next call to next()\r
734      * will be the collation element corresponding to the specified position\r
735      * in the text. Unlike the public method setOffset(int), this method does\r
736      * not try to readjust the offset to the start of a contracting sequence.\r
737      * getOffset() is guaranteed to return the same value as was passed to a\r
738      * preceding call to setOffset().</p>\r
739      * </p>\r
740      * @param source the new source string iterator for iteration.\r
741      * @param offset to the source\r
742      */\r
743     void setText(UCharacterIterator source, int offset)\r
744     {\r
745         m_srcUtilIter_.setText(source.getText());\r
746         m_source_ = m_srcUtilIter_;\r
747         m_source_.setIndex(offset);\r
748         updateInternalState();\r
749     }\r
750 \r
751     // private inner class --------------------------------------------------\r
752 \r
753     /**\r
754      * Backup data class\r
755      */\r
756     private static final class Backup\r
757     {\r
758         // protected data members -------------------------------------------\r
759 \r
760         /**\r
761          * Backup non FCD sequence limit\r
762          */\r
763         protected int m_FCDLimit_;\r
764         /**\r
765          * Backup non FCD sequence start\r
766          */\r
767         protected int m_FCDStart_;\r
768         /**\r
769          * Backup if previous Codepoint is Hiragana quatenary\r
770          */\r
771         protected boolean m_isCodePointHiragana_;\r
772         /**\r
773          * Backup buffer position\r
774          */\r
775         protected int m_bufferOffset_;\r
776         /**\r
777          * Backup source iterator offset\r
778          */\r
779         protected int m_offset_;\r
780         /**\r
781          * Backup buffer contents\r
782          */\r
783         protected StringBuffer m_buffer_;\r
784 \r
785         // protected constructor --------------------------------------------\r
786 \r
787         /**\r
788          * Empty constructor\r
789          */\r
790         protected Backup()\r
791         {\r
792             m_buffer_ = new StringBuffer();\r
793         }\r
794     }\r
795     // end inner class ------------------------------------------------------\r
796 \r
797     /**\r
798      * Direction of travel\r
799      */\r
800     private boolean m_isForwards_;\r
801     /**\r
802      * Source string iterator\r
803      */\r
804     private UCharacterIterator m_source_;\r
805     /**\r
806      * This is position to the m_buffer_, -1 if iterator is not in m_buffer_\r
807      */\r
808     private int m_bufferOffset_;\r
809     /**\r
810      * Buffer for temporary storage of normalized characters, discontiguous\r
811      * characters and Thai characters\r
812      */\r
813     private StringBuffer m_buffer_;\r
814     /**\r
815      * Position in the original string to continue forward FCD check from.\r
816      */\r
817     private int m_FCDLimit_;\r
818     /**\r
819      * The collator this iterator is based on\r
820      */\r
821     private RuleBasedCollator m_collator_;\r
822     /**\r
823      * true if Hiragana quatenary is on\r
824      */\r
825     //private boolean m_isHiragana4_;\r
826     /**\r
827      * CE buffer\r
828      */\r
829     private int m_CEBuffer_[];\r
830     /**\r
831      * In reality we should not have to deal with expansion sequences longer\r
832      * then 16. However this value can be change if a bigger buffer is needed.\r
833      * Note, if the size is change to too small a number, BIG trouble.\r
834      * Reasonable small value is around 10, if there's no Arabic or other\r
835      * funky collations that have long expansion sequence. This is the longest\r
836      * expansion sequence this can handle without bombing out.\r
837      */\r
838     private static final int CE_BUFFER_INIT_SIZE_ = 512;\r
839     /**\r
840      * Backup storage for special processing inner cases\r
841      */\r
842     private Backup m_utilSpecialBackUp_;\r
843     /**\r
844      * Backup storage in special processing entry state\r
845      */\r
846     private Backup m_utilSpecialEntryBackUp_;\r
847     /**\r
848      * Backup storage in special processing discontiguous state\r
849      */\r
850     private Backup m_utilSpecialDiscontiguousBackUp_;\r
851     /**\r
852      * Utility\r
853      */\r
854     private StringUCharacterIterator m_srcUtilIter_;\r
855     private StringBuffer m_utilStringBuffer_;\r
856     private StringBuffer m_utilSkippedBuffer_;\r
857     private CollationElementIterator m_utilColEIter_;\r
858     /**\r
859      * One character before the first non-zero combining class character\r
860      */\r
861     private static final int FULL_ZERO_COMBINING_CLASS_FAST_LIMIT_ = 0xC0;\r
862     /**\r
863      * One character before the first character with leading non-zero combining\r
864      * class\r
865      */\r
866     private static final int LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_ = 0x300;\r
867     /**\r
868      * Mask for the last byte\r
869      */\r
870     private static final int LAST_BYTE_MASK_ = 0xFF;\r
871     /**\r
872      * Shift value for the second last byte\r
873      */\r
874     private static final int SECOND_LAST_BYTE_SHIFT_ = 8;\r
875 \r
876     // special ce values and tags -------------------------------------------\r
877     \r
878 //    private static final int CE_EXPANSION_ = 0xF1000000;\r
879     private static final int CE_CONTRACTION_ = 0xF2000000;\r
880     /**\r
881      * Indicates the last ce has been consumed. Compare with NULLORDER.\r
882      * NULLORDER is returned if error occurs.\r
883      */\r
884 /*    private static final int CE_NO_MORE_CES_ = 0x00010101;\r
885     private static final int CE_NO_MORE_CES_PRIMARY_ = 0x00010000;\r
886     private static final int CE_NO_MORE_CES_SECONDARY_ = 0x00000100;\r
887     private static final int CE_NO_MORE_CES_TERTIARY_ = 0x00000001;\r
888 */\r
889     private static final int CE_NOT_FOUND_TAG_ = 0;\r
890     /**\r
891      * Charset processing, not yet implemented\r
892      */\r
893     private static final int CE_CHARSET_TAG_ = 4;\r
894     /**\r
895      * AC00-D7AF\r
896      */\r
897     private static final int CE_HANGUL_SYLLABLE_TAG_ = 6;\r
898     /**\r
899      * D800-DBFF\r
900      */\r
901     private static final int CE_LEAD_SURROGATE_TAG_ = 7;\r
902     /**\r
903      * DC00-DFFF\r
904      */\r
905     private static final int CE_TRAIL_SURROGATE_TAG_ = 8;\r
906     /**\r
907      * 0x3400-0x4DB5, 0x4E00-0x9FA5, 0xF900-0xFA2D\r
908      */\r
909     private static final int CE_CJK_IMPLICIT_TAG_ = 9;\r
910     private static final int CE_IMPLICIT_TAG_ = 10;\r
911     static final int CE_SPEC_PROC_TAG_ = 11;\r
912     /**\r
913      * This is a 3 byte primary with starting secondaries and tertiaries.\r
914      * It fits in a single 32 bit CE and is used instead of expansion to save\r
915      * space without affecting the performance (hopefully).\r
916      */\r
917     private static final int CE_LONG_PRIMARY_TAG_ = 12;\r
918                         \r
919 //    private static final int CE_CE_TAGS_COUNT = 14;\r
920     private static final int CE_BYTE_COMMON_ = 0x05;\r
921 \r
922     // end special ce values and tags ---------------------------------------\r
923 \r
924     private static final int HANGUL_SBASE_ = 0xAC00;\r
925     private static final int HANGUL_LBASE_ = 0x1100;\r
926     private static final int HANGUL_VBASE_ = 0x1161;\r
927     private static final int HANGUL_TBASE_ = 0x11A7;\r
928     private static final int HANGUL_VCOUNT_ = 21;\r
929     private static final int HANGUL_TCOUNT_ = 28;\r
930 \r
931     // CJK stuff ------------------------------------------------------------\r
932 \r
933 /*    private static final int CJK_BASE_ = 0x4E00;\r
934     private static final int CJK_LIMIT_ = 0x9FFF+1;\r
935     private static final int CJK_COMPAT_USED_BASE_ = 0xFA0E;\r
936     private static final int CJK_COMPAT_USED_LIMIT_ = 0xFA2F + 1;\r
937     private static final int CJK_A_BASE_ = 0x3400;\r
938     private static final int CJK_A_LIMIT_ = 0x4DBF + 1;\r
939     private static final int CJK_B_BASE_ = 0x20000;\r
940     private static final int CJK_B_LIMIT_ = 0x2A6DF + 1;\r
941     private static final int NON_CJK_OFFSET_ = 0x110000;\r
942 */\r
943     private static final boolean DEBUG  =  ICUDebug.enabled("collator");\r
944     \r
945     // private methods ------------------------------------------------------\r
946 \r
947     /**\r
948      * Reset the iterator internally\r
949      */\r
950     private void updateInternalState()\r
951     {\r
952         m_isCodePointHiragana_ = false;\r
953         m_buffer_.setLength(0);\r
954         m_bufferOffset_ = -1;\r
955         m_CEBufferOffset_ = 0;\r
956         m_CEBufferSize_ = 0;\r
957         m_FCDLimit_ = -1;\r
958         m_FCDStart_ = m_source_.getLength();\r
959         //m_isHiragana4_ = m_collator_.m_isHiragana4_;\r
960         m_isForwards_ = true;\r
961     }\r
962 \r
963     /**\r
964      * Backup the current internal state\r
965      * @param backup object to store the data\r
966      */\r
967     private void backupInternalState(Backup backup)\r
968     {\r
969         backup.m_offset_ = m_source_.getIndex();\r
970         backup.m_FCDLimit_ = m_FCDLimit_;\r
971         backup.m_FCDStart_ = m_FCDStart_;\r
972         backup.m_isCodePointHiragana_ = m_isCodePointHiragana_;\r
973         backup.m_bufferOffset_ = m_bufferOffset_;\r
974         backup.m_buffer_.setLength(0);\r
975         if (m_bufferOffset_ >= 0) {\r
976             // jdk 1.3.1 does not have append(StringBuffer) yet\r
977             if(ICUDebug.isJDK14OrHigher){\r
978                 backup.m_buffer_.append(m_buffer_);\r
979             }else{\r
980                 backup.m_buffer_.append(m_buffer_.toString());\r
981             }\r
982         }\r
983     }\r
984 \r
985     /**\r
986      * Update the iterator internally with backed-up state\r
987      * @param backup object that stored the data\r
988      */\r
989     private void updateInternalState(Backup backup)\r
990     {\r
991         m_source_.setIndex(backup.m_offset_);\r
992         m_isCodePointHiragana_ = backup.m_isCodePointHiragana_;\r
993         m_bufferOffset_ = backup.m_bufferOffset_;\r
994         m_FCDLimit_ = backup.m_FCDLimit_;\r
995         m_FCDStart_ = backup.m_FCDStart_;\r
996         m_buffer_.setLength(0);\r
997         if (m_bufferOffset_ >= 0) {\r
998             // jdk 1.3.1 does not have append(StringBuffer) yet\r
999             m_buffer_.append(backup.m_buffer_.toString());\r
1000         }\r
1001     }\r
1002 \r
1003     /**\r
1004      * A fast combining class retrieval system.\r
1005      * @param ch UTF16 character\r
1006      * @return combining class of ch\r
1007      */\r
1008     private int getCombiningClass(int ch)\r
1009     {\r
1010         if (ch >= LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_ &&\r
1011             m_collator_.isUnsafe((char)ch) || ch > 0xFFFF) {\r
1012             return NormalizerImpl.getCombiningClass(ch);\r
1013         }\r
1014         return 0;\r
1015     }\r
1016 \r
1017     /**\r
1018      * <p>Incremental normalization, this is an essential optimization.\r
1019      * Assuming FCD checks has been done, normalize the non-FCD characters into\r
1020      * the buffer.\r
1021      * Source offsets points to the current processing character.\r
1022      * </p>\r
1023      */\r
1024     private void normalize()\r
1025     {\r
1026         int size = m_FCDLimit_ - m_FCDStart_;\r
1027         m_buffer_.setLength(0);\r
1028         m_source_.setIndex(m_FCDStart_);\r
1029         for (int i = 0; i < size; i ++) {\r
1030             m_buffer_.append((char)m_source_.next());\r
1031         }\r
1032         String decomp = Normalizer.decompose(m_buffer_.toString(), false);\r
1033         m_buffer_.setLength(0);\r
1034         m_buffer_.append(decomp);\r
1035         m_bufferOffset_ = 0;\r
1036     }\r
1037 \r
1038     /**\r
1039      * <p>Incremental FCD check and normalization. Gets the next base character\r
1040      * position and determines if the in-between characters needs normalization.\r
1041      * </p>\r
1042      * <p>When entering, the state is known to be this:\r
1043      * <ul>\r
1044      * <li>We are working on source string, not the buffer.\r
1045      * <li>The leading combining class from the current character is 0 or the\r
1046      *     trailing combining class of the previous char was zero.\r
1047      * </ul>\r
1048      * Incoming source offsets points to the current processing character.\r
1049      * Return source offsets points to the current processing character.\r
1050      * </p>\r
1051      * @param ch current character\r
1052      * @param offset current character offset\r
1053      * @return true if FCDCheck passes, false otherwise\r
1054      */\r
1055     private boolean FCDCheck(char ch, int offset)\r
1056     {\r
1057         boolean result = true;\r
1058 \r
1059         // Get the trailing combining class of the current character.\r
1060         // If it's zero, we are OK.\r
1061         m_FCDStart_ = offset;\r
1062         m_source_.setIndex(offset);\r
1063         // trie access\r
1064         char fcd = NormalizerImpl.getFCD16(ch);\r
1065         if (fcd != 0 && UTF16.isLeadSurrogate(ch)) {\r
1066             m_source_.next();\r
1067             ch = (char)m_source_.current(); \r
1068             // UCharacterIterator.DONE has 0 fcd\r
1069             if (UTF16.isTrailSurrogate(ch)) {\r
1070                 fcd = NormalizerImpl.getFCD16FromSurrogatePair(fcd, ch);\r
1071             } else {\r
1072                 fcd = 0;\r
1073             }\r
1074         }\r
1075 \r
1076         int prevTrailCC = fcd & LAST_BYTE_MASK_;\r
1077 \r
1078         if (prevTrailCC != 0) {\r
1079             // The current char has a non-zero trailing CC. Scan forward until\r
1080             // we find a char with a leading cc of zero.\r
1081             while (true) {\r
1082                 m_source_.next();\r
1083                 int ch_int = m_source_.current();\r
1084                 if (ch_int == UCharacterIterator.DONE) {\r
1085                     break;\r
1086                 }\r
1087                 ch = (char)ch_int;\r
1088                 // trie access\r
1089                 fcd = NormalizerImpl.getFCD16(ch);\r
1090                 if (fcd != 0 && UTF16.isLeadSurrogate(ch)) {\r
1091                     m_source_.next();\r
1092                     ch = (char)m_source_.current();\r
1093                     if (UTF16.isTrailSurrogate(ch)) {\r
1094                         fcd = NormalizerImpl.getFCD16FromSurrogatePair(fcd, ch);\r
1095                     } else {\r
1096                         fcd = 0;\r
1097                     }\r
1098                 }\r
1099                 int leadCC = fcd >>> SECOND_LAST_BYTE_SHIFT_;\r
1100                 if (leadCC == 0) {\r
1101                     // this is a base character, we stop the FCD checks\r
1102                     break;\r
1103                 }\r
1104 \r
1105                 if (leadCC < prevTrailCC) {\r
1106                     result = false;\r
1107                 }\r
1108 \r
1109                 prevTrailCC = fcd & LAST_BYTE_MASK_;\r
1110             }\r
1111         }\r
1112         m_FCDLimit_ = m_source_.getIndex();\r
1113         m_source_.setIndex(m_FCDStart_);\r
1114         m_source_.next();\r
1115         return result;\r
1116     }\r
1117 \r
1118     /**\r
1119      * <p>Method tries to fetch the next character that is in fcd form.</p>\r
1120      * <p>Normalization is done if required.</p>\r
1121      * <p>Offsets are returned at the next character.</p>\r
1122      * @return next fcd character\r
1123      */\r
1124     private int nextChar()\r
1125     {\r
1126         int result;\r
1127 \r
1128         // loop handles the next character whether it is in the buffer or not.\r
1129         if (m_bufferOffset_ < 0) {\r
1130             // we're working on the source and not normalizing. fast path.\r
1131             // note Thai pre-vowel reordering uses buffer too\r
1132             result = m_source_.current();\r
1133         }\r
1134         else {\r
1135             // we are in the buffer, buffer offset will never be 0 here\r
1136             if (m_bufferOffset_ >= m_buffer_.length()) {\r
1137                 // Null marked end of buffer, revert to the source string and\r
1138                 // loop back to top to try again to get a character.\r
1139                 m_source_.setIndex(m_FCDLimit_);\r
1140                 m_bufferOffset_ = -1;\r
1141                 m_buffer_.setLength(0);\r
1142                 return nextChar();\r
1143             }\r
1144             return m_buffer_.charAt(m_bufferOffset_ ++);\r
1145         }\r
1146         int startoffset = m_source_.getIndex();\r
1147         if (result < FULL_ZERO_COMBINING_CLASS_FAST_LIMIT_\r
1148             // Fast fcd safe path. trail combining class == 0.\r
1149             || m_collator_.getDecomposition() == Collator.NO_DECOMPOSITION\r
1150             || m_bufferOffset_ >= 0 || m_FCDLimit_ > startoffset) {\r
1151             // skip the fcd checks\r
1152             m_source_.next();\r
1153             return result;\r
1154         }\r
1155 \r
1156         if (result < LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_) {\r
1157             // We need to peek at the next character in order to tell if we are\r
1158             // FCD\r
1159             m_source_.next();\r
1160             int next = m_source_.current();\r
1161             if (next == UCharacterIterator.DONE\r
1162                 || next < LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_) {\r
1163                 return result; // end of source string and if next character\r
1164                 // starts with a base character is always fcd.\r
1165             }\r
1166         }\r
1167 \r
1168         // Need a more complete FCD check and possible normalization.\r
1169         if (!FCDCheck((char)result, startoffset)) {\r
1170             normalize();\r
1171             result = m_buffer_.charAt(0);\r
1172             m_bufferOffset_ = 1;\r
1173         }\r
1174         return result;\r
1175     }\r
1176 \r
1177     /**\r
1178      * <p>Incremental normalization, this is an essential optimization.\r
1179      * Assuming FCD checks has been done, normalize the non-FCD characters into\r
1180      * the buffer.\r
1181      * Source offsets points to the current processing character.</p>\r
1182      */\r
1183     private void normalizeBackwards()\r
1184     {\r
1185         normalize();\r
1186         m_bufferOffset_ = m_buffer_.length();\r
1187     }\r
1188 \r
1189     /**\r
1190      * <p>Incremental backwards FCD check and normalization. Gets the previous\r
1191      * base character position and determines if the in-between characters\r
1192      * needs normalization.\r
1193      * </p>\r
1194      * <p>When entering, the state is known to be this:\r
1195      * <ul>\r
1196      * <li>We are working on source string, not the buffer.\r
1197      * <li>The trailing combining class from the current character is 0 or the\r
1198      *     leading combining class of the next char was zero.\r
1199      * </ul>\r
1200      * Input source offsets points to the previous character.\r
1201      * Return source offsets points to the current processing character.\r
1202      * </p>\r
1203      * @param ch current character\r
1204      * @param offset current character offset\r
1205      * @return true if FCDCheck passes, false otherwise\r
1206      */\r
1207     private boolean FCDCheckBackwards(char ch, int offset)\r
1208     {\r
1209         boolean result = true;\r
1210         char fcd = 0;\r
1211         m_FCDLimit_ = offset + 1;\r
1212         m_source_.setIndex(offset);\r
1213         if (!UTF16.isSurrogate(ch)) {\r
1214             fcd = NormalizerImpl.getFCD16(ch);\r
1215         }\r
1216         else if (UTF16.isTrailSurrogate(ch) && m_FCDLimit_ > 0) {\r
1217             // note trail surrogate characters gets 0 fcd\r
1218             char trailch = ch;\r
1219             ch = (char)m_source_.previous();\r
1220             if (UTF16.isLeadSurrogate(ch)) {\r
1221                 fcd = NormalizerImpl.getFCD16(ch);\r
1222                 if (fcd != 0) {\r
1223                     fcd = NormalizerImpl.getFCD16FromSurrogatePair(fcd,\r
1224                                                                    trailch);\r
1225                 }\r
1226             }\r
1227             else {\r
1228                 fcd = 0; // unpaired surrogate\r
1229             }\r
1230         }\r
1231 \r
1232         int leadCC = fcd >>> SECOND_LAST_BYTE_SHIFT_;\r
1233         // The current char has a non-zero leading combining class.\r
1234         // Scan backward until we find a char with a trailing cc of zero.\r
1235 \r
1236         while (leadCC != 0) {\r
1237             offset = m_source_.getIndex();\r
1238             if (offset == 0) {\r
1239                 break;\r
1240             }\r
1241             ch = (char)m_source_.previous();\r
1242             if (!UTF16.isSurrogate(ch)) {\r
1243                 fcd = NormalizerImpl.getFCD16(ch);\r
1244             }\r
1245             else if (UTF16.isTrailSurrogate(ch) && m_source_.getIndex() > 0) {\r
1246                 char trail = ch;\r
1247                 ch = (char)m_source_.previous();\r
1248                 if (UTF16.isLeadSurrogate(ch)) {\r
1249                     fcd = NormalizerImpl.getFCD16(ch);\r
1250                 }\r
1251                 if (fcd != 0) {\r
1252                     fcd = NormalizerImpl.getFCD16FromSurrogatePair(fcd, trail);\r
1253                 }\r
1254             }\r
1255             else {\r
1256                 fcd = 0; // unpaired surrogate\r
1257             }\r
1258             int prevTrailCC = fcd & LAST_BYTE_MASK_;\r
1259             if (leadCC < prevTrailCC) {\r
1260                 result = false;\r
1261             }\r
1262             leadCC = fcd >>> SECOND_LAST_BYTE_SHIFT_;\r
1263         }\r
1264 \r
1265         // storing character with 0 lead fcd or the 1st accent with a base\r
1266         // character before it\r
1267         if (fcd == 0) {\r
1268             m_FCDStart_ = offset;\r
1269         }\r
1270         else {\r
1271             m_FCDStart_ = m_source_.getIndex();\r
1272         }\r
1273         m_source_.setIndex(m_FCDLimit_);\r
1274         return result;\r
1275     }\r
1276 \r
1277     /**\r
1278      * <p>Method tries to fetch the previous character that is in fcd form.</p>\r
1279      * <p>Normalization is done if required.</p>\r
1280      * <p>Offsets are returned at the current character.</p>\r
1281      * @return previous fcd character\r
1282      */\r
1283     private int previousChar()\r
1284     {\r
1285         if (m_bufferOffset_ >= 0) {\r
1286             m_bufferOffset_ --;\r
1287             if (m_bufferOffset_ >= 0) {\r
1288                 return m_buffer_.charAt(m_bufferOffset_);\r
1289             }\r
1290             else {\r
1291                 // At the start of buffer, route back to string.\r
1292                 m_buffer_.setLength(0);\r
1293                 if (m_FCDStart_ == 0) {\r
1294                     m_FCDStart_ = -1;\r
1295                     m_source_.setIndex(0);\r
1296                     return UCharacterIterator.DONE;\r
1297                 }\r
1298                 else {\r
1299                     m_FCDLimit_ = m_FCDStart_;\r
1300                     m_source_.setIndex(m_FCDStart_);\r
1301                     return previousChar();\r
1302                 }\r
1303             }\r
1304         }\r
1305         int result = m_source_.previous();\r
1306         int startoffset = m_source_.getIndex();\r
1307         if (result < LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_\r
1308             || m_collator_.getDecomposition() == Collator.NO_DECOMPOSITION\r
1309             || m_FCDStart_ <= startoffset || m_source_.getIndex() == 0) {\r
1310             return result;\r
1311         }\r
1312         int ch = m_source_.previous();\r
1313         if (ch < FULL_ZERO_COMBINING_CLASS_FAST_LIMIT_) {\r
1314             // if previous character is FCD\r
1315             m_source_.next();\r
1316             return result;\r
1317         }\r
1318         // Need a more complete FCD check and possible normalization.\r
1319         if (!FCDCheckBackwards((char)result, startoffset)) {\r
1320             normalizeBackwards();\r
1321             m_bufferOffset_ --;\r
1322             result = m_buffer_.charAt(m_bufferOffset_);\r
1323         }\r
1324         else {\r
1325             // fcd checks alway reset m_source_ to the limit of the FCD\r
1326             m_source_.setIndex(startoffset);\r
1327         }\r
1328         return result;\r
1329     }\r
1330 \r
1331     /**\r
1332      * Determines if it is at the start of source iteration\r
1333      * @return true if iterator at the start, false otherwise\r
1334      */\r
1335     private final boolean isBackwardsStart()\r
1336     {\r
1337         return (m_bufferOffset_ < 0 && m_source_.getIndex() == 0)\r
1338             || (m_bufferOffset_ == 0 && m_FCDStart_ <= 0);\r
1339     }\r
1340 \r
1341     /**\r
1342      * Checks if iterator is at the end of its source string.\r
1343      * @return true if it is at the end, false otherwise\r
1344      */\r
1345     private final boolean isEnd()\r
1346     {\r
1347         if (m_bufferOffset_ >= 0) {\r
1348             if (m_bufferOffset_ != m_buffer_.length()) {\r
1349                 return false;\r
1350             }\r
1351             else {\r
1352                 // at end of buffer. check if fcd is at the end\r
1353                 return m_FCDLimit_ == m_source_.getLength();\r
1354             }\r
1355         }\r
1356         return m_source_.getLength() == m_source_.getIndex();\r
1357     }\r
1358 \r
1359     /**\r
1360      * <p>Special CE management for surrogates</p>\r
1361      * <p>Lead surrogate is encountered. CE to be retrieved by using the\r
1362      * following code unit. If next character is a trail surrogate, both\r
1363      * characters will be combined to retrieve the CE, otherwise completely\r
1364      * ignorable (UCA specification) is returned.</p>\r
1365      * @param collator collator to use\r
1366      * @param ce current CE\r
1367      * @param trail character\r
1368      * @return next CE for the surrogate characters\r
1369      */\r
1370     private final int nextSurrogate(RuleBasedCollator collator, int ce,\r
1371                                     char trail)\r
1372     {\r
1373         if (!UTF16.isTrailSurrogate(trail)) {\r
1374             updateInternalState(m_utilSpecialBackUp_);\r
1375             return IGNORABLE;\r
1376         }\r
1377         // TODO: CE contain the data from the previous CE + the mask.\r
1378         // It should at least be unmasked\r
1379         int result = collator.m_trie_.getTrailValue(ce, trail);\r
1380         if (result == CE_NOT_FOUND_) {\r
1381             updateInternalState(m_utilSpecialBackUp_);\r
1382         }\r
1383         return result;\r
1384     }\r
1385 \r
1386     /**\r
1387      * Gets the CE expansion offset\r
1388      * @param collator current collator\r
1389      * @param ce ce to test\r
1390      * @return expansion offset\r
1391      */\r
1392     private int getExpansionOffset(RuleBasedCollator collator, int ce)\r
1393     {\r
1394         return ((ce & 0xFFFFF0) >> 4) - collator.m_expansionOffset_;\r
1395     }\r
1396 \r
1397 \r
1398     /**\r
1399      * Gets the contraction ce offset\r
1400      * @param collator current collator\r
1401      * @param ce current ce\r
1402      * @return contraction offset\r
1403      */\r
1404     private int getContractionOffset(RuleBasedCollator collator, int ce)\r
1405     {\r
1406         return (ce & 0xFFFFFF) - collator.m_contractionOffset_;\r
1407     }\r
1408 \r
1409     /**\r
1410      * Checks if CE is a special tag CE\r
1411      * @param ce to check\r
1412      * @return true if CE is a special tag CE, false otherwise\r
1413      */\r
1414     private boolean isSpecialPrefixTag(int ce)\r
1415     {\r
1416         return RuleBasedCollator.isSpecial(ce) &&\r
1417             RuleBasedCollator.getTag(ce) == CE_SPEC_PROC_TAG_;\r
1418     }\r
1419 \r
1420     /**\r
1421      * <p>Special processing getting a CE that is preceded by a certain\r
1422      * prefix.</p>\r
1423      * <p>Used for optimizing Japanese length and iteration marks. When a\r
1424      * special processing tag is encountered, iterate backwards to see if\r
1425      * there's a match.</p>\r
1426      * <p>Contraction tables are used, prefix data is stored backwards in the\r
1427      * table.</p>\r
1428      * @param collator collator to use\r
1429      * @param ce current ce\r
1430      * @param entrybackup entry backup iterator status\r
1431      * @return next collation element\r
1432      */\r
1433     private int nextSpecialPrefix(RuleBasedCollator collator, int ce,\r
1434                                   Backup entrybackup)\r
1435     {\r
1436         backupInternalState(m_utilSpecialBackUp_);\r
1437         updateInternalState(entrybackup);\r
1438         previousChar();\r
1439         // We want to look at the character where we entered\r
1440 \r
1441         while (true) {\r
1442             // This loop will run once per source string character, for as\r
1443             // long as we are matching a potential contraction sequence\r
1444             // First we position ourselves at the begining of contraction\r
1445             // sequence\r
1446             int entryoffset = getContractionOffset(collator, ce);\r
1447             int offset = entryoffset;\r
1448             if (isBackwardsStart()) {\r
1449                 ce = collator.m_contractionCE_[offset];\r
1450                 break;\r
1451             }\r
1452             char previous = (char)previousChar();\r
1453             while (previous > collator.m_contractionIndex_[offset]) {\r
1454                 // contraction characters are ordered, skip smaller characters\r
1455                 offset ++;\r
1456             }\r
1457 \r
1458             if (previous == collator.m_contractionIndex_[offset]) {\r
1459                 // Found the source string char in the table.\r
1460                 // Pick up the corresponding CE from the table.\r
1461                 ce = collator.m_contractionCE_[offset];\r
1462             }\r
1463             else {\r
1464                 // Source string char was not in the table, prefix not found\r
1465                 ce = collator.m_contractionCE_[entryoffset];\r
1466             }\r
1467 \r
1468             if (!isSpecialPrefixTag(ce)) {\r
1469                 // The source string char was in the contraction table, and\r
1470                 // the corresponding CE is not a prefix CE. We found the\r
1471                 // prefix, break out of loop, this CE will end up being\r
1472                 // returned. This is the normal way out of prefix handling\r
1473                 // when the source actually contained the prefix.\r
1474                 break;\r
1475             }\r
1476         }\r
1477         if (ce != CE_NOT_FOUND_) {\r
1478             // we found something and we can merilly continue\r
1479             updateInternalState(m_utilSpecialBackUp_);\r
1480         }\r
1481         else { // prefix search was a failure, we have to backup all the way to\r
1482             // the start\r
1483             updateInternalState(entrybackup);\r
1484         }\r
1485         return ce;\r
1486     }\r
1487 \r
1488     /**\r
1489      * Checks if the ce is a contraction tag\r
1490      * @param ce ce to check\r
1491      * @return true if ce is a contraction tag, false otherwise\r
1492      */\r
1493     private boolean isContractionTag(int ce)\r
1494     {\r
1495         return RuleBasedCollator.isSpecial(ce) &&\r
1496             RuleBasedCollator.getTag(ce) == CE_CONTRACTION_TAG_;\r
1497     }\r
1498 \r
1499     /**\r
1500      * Method to copy skipped characters into the buffer and sets the fcd\r
1501      * position. To ensure that the skipped characters are considered later,\r
1502      * we need to place it in the appropriate position in the buffer and\r
1503      * reassign the source index. simple case if index reside in string,\r
1504      * simply copy to buffer and fcdposition = pos, pos = start of buffer.\r
1505      * if pos in normalization buffer, we'll insert the copy infront of pos\r
1506      * and point pos to the start of the buffer. why am i doing these copies?\r
1507      * well, so that the whole chunk of codes in the getNextCE,\r
1508      * ucol_prv_getSpecialCE does not require any changes, which will be\r
1509      * really painful.\r
1510      * @param skipped character buffer\r
1511      */\r
1512     private void setDiscontiguous(StringBuffer skipped)\r
1513     {\r
1514         if (m_bufferOffset_ >= 0) {\r
1515             m_buffer_.replace(0, m_bufferOffset_, skipped.toString());\r
1516         }\r
1517         else {\r
1518             m_FCDLimit_ = m_source_.getIndex();\r
1519             m_buffer_.setLength(0);\r
1520             m_buffer_.append(skipped.toString());\r
1521         }\r
1522 \r
1523         m_bufferOffset_ = 0;\r
1524     }\r
1525 \r
1526     /**\r
1527      * Returns the current character for forward iteration\r
1528      * @return current character\r
1529      */\r
1530     private int currentChar()\r
1531     {\r
1532         if (m_bufferOffset_ < 0) {\r
1533             m_source_.previous();\r
1534             return m_source_.next();\r
1535         }\r
1536 \r
1537         // m_bufferOffset_ is never 0 in normal circumstances except after a\r
1538         // discontiguous contraction since it is always returned and moved\r
1539         // by 1 when we do nextChar()\r
1540         return m_buffer_.charAt(m_bufferOffset_ - 1);\r
1541     }\r
1542 \r
1543     /**\r
1544      * Method to get the discontiguous collation element within the source.\r
1545      * Note this function will set the position to the appropriate places.\r
1546      * Passed in character offset points to the second combining character\r
1547      * after the start character.\r
1548      * @param collator current collator used\r
1549      * @param entryoffset index to the start character in the contraction table\r
1550      * @return discontiguous collation element offset\r
1551      */\r
1552     private int nextDiscontiguous(RuleBasedCollator collator, int entryoffset)\r
1553     {\r
1554         int offset = entryoffset;\r
1555         boolean multicontraction = false;\r
1556         // since it will be stuffed into this iterator and ran over again\r
1557         if (m_utilSkippedBuffer_ == null) {\r
1558             m_utilSkippedBuffer_ = new StringBuffer();\r
1559         }\r
1560         else {\r
1561             m_utilSkippedBuffer_.setLength(0);\r
1562         }\r
1563         char ch = (char)currentChar();\r
1564         m_utilSkippedBuffer_.append((char)currentChar());\r
1565         // accent after the first character\r
1566         if (m_utilSpecialDiscontiguousBackUp_ == null) {\r
1567             m_utilSpecialDiscontiguousBackUp_ = new Backup();\r
1568         }\r
1569         backupInternalState(m_utilSpecialDiscontiguousBackUp_);\r
1570         char nextch = ch;\r
1571         while (true) {\r
1572             ch = nextch;\r
1573             int ch_int = nextChar();\r
1574             nextch = (char)ch_int;\r
1575             if (ch_int == UCharacterIterator.DONE\r
1576                 || getCombiningClass(nextch) == 0) {\r
1577                 // if there are no more accents to move around\r
1578                 // we don't have to shift previousChar, since we are resetting\r
1579                 // the offset later\r
1580                 if (multicontraction) {\r
1581                     if (ch_int != UCharacterIterator.DONE) {\r
1582                         previousChar(); // backtrack\r
1583                     }\r
1584                     setDiscontiguous(m_utilSkippedBuffer_);\r
1585                     return collator.m_contractionCE_[offset];\r
1586                 }\r
1587                 break;\r
1588             }\r
1589 \r
1590             offset ++; // skip the combining class offset\r
1591             while ((offset < collator.m_contractionIndex_.length) &&\r
1592                    (nextch > collator.m_contractionIndex_[offset])) {\r
1593                 offset ++;\r
1594             }\r
1595 \r
1596             int ce = CE_NOT_FOUND_;\r
1597             if ( offset >= collator.m_contractionIndex_.length)  {\r
1598                 break;\r
1599             }\r
1600             if ( nextch != collator.m_contractionIndex_[offset]\r
1601                  || getCombiningClass(nextch) == getCombiningClass(ch)) {\r
1602                     // unmatched or blocked character\r
1603                 if ( (m_utilSkippedBuffer_.length()!= 1) ||\r
1604                      ((m_utilSkippedBuffer_.charAt(0)!= nextch) &&\r
1605                       (m_bufferOffset_<0) )) { // avoid push to skipped buffer twice\r
1606                     m_utilSkippedBuffer_.append(nextch);\r
1607                 }\r
1608                 offset = entryoffset;  // Restore the offset before checking next character.\r
1609                 continue;\r
1610             }\r
1611             else {\r
1612                 ce = collator.m_contractionCE_[offset];\r
1613             }\r
1614 \r
1615             if (ce == CE_NOT_FOUND_) {\r
1616                 break;\r
1617             }\r
1618             else if (isContractionTag(ce)) {\r
1619                 // this is a multi-contraction\r
1620                 offset = getContractionOffset(collator, ce);\r
1621                 if (collator.m_contractionCE_[offset] != CE_NOT_FOUND_) {\r
1622                     multicontraction = true;\r
1623                     backupInternalState(m_utilSpecialDiscontiguousBackUp_);\r
1624                 }\r
1625             }\r
1626             else {\r
1627                 setDiscontiguous(m_utilSkippedBuffer_);\r
1628                 return ce;\r
1629             }\r
1630         }\r
1631 \r
1632         updateInternalState(m_utilSpecialDiscontiguousBackUp_);\r
1633         // backup is one forward of the base character, we need to move back\r
1634         // one more\r
1635         previousChar();\r
1636         return collator.m_contractionCE_[entryoffset];\r
1637     }\r
1638 \r
1639     /**\r
1640      * Gets the next contraction ce\r
1641      * @param collator collator to use\r
1642      * @param ce current ce\r
1643      * @return ce of the next contraction\r
1644      */\r
1645     private int nextContraction(RuleBasedCollator collator, int ce)\r
1646     {\r
1647         backupInternalState(m_utilSpecialBackUp_);\r
1648         int entryce = collator.m_contractionCE_[getContractionOffset(collator, ce)]; //CE_NOT_FOUND_;\r
1649         while (true) {\r
1650             int entryoffset = getContractionOffset(collator, ce);\r
1651             int offset = entryoffset;\r
1652 \r
1653             if (isEnd()) {\r
1654                 ce = collator.m_contractionCE_[offset];\r
1655                 if (ce == CE_NOT_FOUND_) {\r
1656                     // back up the source over all the chars we scanned going\r
1657                     // into this contraction.\r
1658                     ce = entryce;\r
1659                     updateInternalState(m_utilSpecialBackUp_);\r
1660                 }\r
1661                 break;\r
1662             }\r
1663 \r
1664             // get the discontiguos maximum combining class\r
1665             int maxCC = (collator.m_contractionIndex_[offset] & 0xFF);\r
1666             // checks if all characters have the same combining class\r
1667             byte allSame = (byte)(collator.m_contractionIndex_[offset] >> 8);\r
1668             char ch = (char)nextChar();\r
1669             offset ++;\r
1670             while (ch > collator.m_contractionIndex_[offset]) {\r
1671                 // contraction characters are ordered, skip all smaller\r
1672                 offset ++;\r
1673             }\r
1674 \r
1675             if (ch == collator.m_contractionIndex_[offset]) {\r
1676                 // Found the source string char in the contraction table.\r
1677                 //  Pick up the corresponding CE from the table.\r
1678                 ce = collator.m_contractionCE_[offset];\r
1679             }\r
1680             else {\r
1681                 // Source string char was not in contraction table.\r
1682                 // Unless it is a discontiguous contraction, we are done\r
1683                 int miss = ch;\r
1684                 if(UTF16.isLeadSurrogate(ch)) { // in order to do the proper detection, we\r
1685                     // need to see if we're dealing with a supplementary\r
1686                     miss = UCharacterProperty.getRawSupplementary(ch, (char) nextChar());\r
1687                   }\r
1688                 int sCC;\r
1689                 if (maxCC == 0 || (sCC = getCombiningClass(miss)) == 0\r
1690                     || sCC > maxCC || (allSame != 0 && sCC == maxCC) ||\r
1691                     isEnd()) {\r
1692                     // Contraction can not be discontiguous, back up by one\r
1693                     previousChar();\r
1694                     if(miss > 0xFFFF) {\r
1695                         previousChar();\r
1696                     }\r
1697                     ce = collator.m_contractionCE_[entryoffset];\r
1698                 }\r
1699                 else {\r
1700                     // Contraction is possibly discontiguous.\r
1701                     // find the next character if ch is not a base character\r
1702                     int ch_int = nextChar();\r
1703                     if (ch_int != UCharacterIterator.DONE) {\r
1704                         previousChar();\r
1705                     }\r
1706                     char nextch = (char)ch_int;\r
1707                     if (getCombiningClass(nextch) == 0) {\r
1708                         previousChar();\r
1709                         if(miss > 0xFFFF) {\r
1710                             previousChar();\r
1711                         }    \r
1712                         // base character not part of discontiguous contraction\r
1713                         ce = collator.m_contractionCE_[entryoffset];\r
1714                     }\r
1715                     else {\r
1716                         ce = nextDiscontiguous(collator, entryoffset);\r
1717                     }\r
1718                 }\r
1719             }\r
1720 \r
1721             if (ce == CE_NOT_FOUND_) {\r
1722                 // source did not match the contraction, revert back original\r
1723                 updateInternalState(m_utilSpecialBackUp_);\r
1724                 ce = entryce;\r
1725                 break;\r
1726             }\r
1727 \r
1728             // source was a contraction\r
1729             if (!isContractionTag(ce)) {\r
1730                 break;\r
1731             }\r
1732 \r
1733             // ccontinue looping to check for the remaining contraction.\r
1734             if (collator.m_contractionCE_[entryoffset] != CE_NOT_FOUND_) {\r
1735                 // there are further contractions to be performed, so we store\r
1736                 // the so-far completed ce, so that if we fail in the next\r
1737                 // round we just return this one.\r
1738                 entryce = collator.m_contractionCE_[entryoffset];\r
1739                 backupInternalState(m_utilSpecialBackUp_);\r
1740                 if (m_utilSpecialBackUp_.m_bufferOffset_ >= 0) {\r
1741                     m_utilSpecialBackUp_.m_bufferOffset_ --;\r
1742                 }\r
1743                 else {\r
1744                     m_utilSpecialBackUp_.m_offset_ --;\r
1745                 }\r
1746             }\r
1747         }\r
1748         return ce;\r
1749     }\r
1750 \r
1751     /**\r
1752      * Gets the next ce for long primaries, stuffs the rest of the collation\r
1753      * elements into the ce buffer\r
1754      * @param ce current ce\r
1755      * @return next ce\r
1756      */\r
1757     private int nextLongPrimary(int ce)\r
1758     {\r
1759         m_CEBuffer_[1] = ((ce & 0xFF) << 24)\r
1760             | RuleBasedCollator.CE_CONTINUATION_MARKER_;\r
1761         m_CEBufferOffset_ = 1;\r
1762         m_CEBufferSize_ = 2;\r
1763         m_CEBuffer_[0] = ((ce & 0xFFFF00) << 8) | (CE_BYTE_COMMON_ << 8) |\r
1764             CE_BYTE_COMMON_;\r
1765         return m_CEBuffer_[0];\r
1766     }\r
1767 \r
1768     /**\r
1769      * Gets the number of expansion\r
1770      * @param ce current ce\r
1771      * @return number of expansion\r
1772      */\r
1773     private int getExpansionCount(int ce)\r
1774     {\r
1775         return ce & 0xF;\r
1776     }\r
1777 \r
1778     /**\r
1779      * Gets the next expansion ce and stuffs the rest of the collation elements\r
1780      * into the ce buffer\r
1781      * @param collator current collator\r
1782      * @param ce current ce\r
1783      * @return next expansion ce\r
1784      */\r
1785     private int nextExpansion(RuleBasedCollator collator, int ce)\r
1786     {\r
1787         // NOTE: we can encounter both continuations and expansions in an\r
1788         // expansion!\r
1789         // I have to decide where continuations are going to be dealt with\r
1790         int offset = getExpansionOffset(collator, ce);\r
1791         m_CEBufferSize_ = getExpansionCount(ce);\r
1792         m_CEBufferOffset_ = 1;\r
1793         m_CEBuffer_[0] = collator.m_expansion_[offset];\r
1794         if (m_CEBufferSize_ != 0) {\r
1795             // if there are less than 16 elements in expansion\r
1796             for (int i = 1; i < m_CEBufferSize_; i ++) {\r
1797                 m_CEBuffer_[i] = collator.m_expansion_[offset + i];\r
1798             }\r
1799         }\r
1800         else {\r
1801             // ce are terminated\r
1802             m_CEBufferSize_ = 1;\r
1803             while (collator.m_expansion_[offset] != 0) {\r
1804                 m_CEBuffer_[m_CEBufferSize_ ++] =\r
1805                     collator.m_expansion_[++ offset];\r
1806             }\r
1807         }\r
1808         // in case of one element expansion, we \r
1809         // want to immediately return CEpos\r
1810         if (m_CEBufferSize_ == 1) {\r
1811             m_CEBufferSize_ = 0;\r
1812             m_CEBufferOffset_ = 0;\r
1813         }\r
1814         return m_CEBuffer_[0];\r
1815     }\r
1816     \r
1817     /**\r
1818      * Gets the next digit ce\r
1819      * @param collator current collator\r
1820      * @param ce current collation element\r
1821      * @param cp current codepoint\r
1822      * @return next digit ce\r
1823      */\r
1824     private int nextDigit(RuleBasedCollator collator, int ce, int cp)\r
1825     {\r
1826         // We do a check to see if we want to collate digits as numbers; \r
1827         // if so we generate a custom collation key. Otherwise we pull out \r
1828         // the value stored in the expansion table.\r
1829 \r
1830         if (m_collator_.m_isNumericCollation_){\r
1831             int collateVal = 0;\r
1832             int trailingZeroIndex = 0;\r
1833             boolean nonZeroValReached = false;\r
1834 \r
1835             // I just need a temporary place to store my generated CEs.\r
1836             // icu4c uses a unsigned byte array, i'll use a stringbuffer here\r
1837             // to avoid dealing with the sign problems and array allocation\r
1838             // clear and set initial string buffer length\r
1839             m_utilStringBuffer_.setLength(3);\r
1840         \r
1841             // We parse the source string until we hit a char that's NOT a \r
1842             // digit.\r
1843             // Use this u_charDigitValue. This might be slow because we have \r
1844             // to handle surrogates...\r
1845             int digVal = UCharacter.digit(cp); \r
1846             // if we have arrived here, we have already processed possible \r
1847             // supplementaries that trigered the digit tag -\r
1848             // all supplementaries are marked in the UCA.\r
1849             // We  pad a zero in front of the first element anyways. \r
1850             // This takes care of the (probably) most common case where \r
1851             // people are sorting things followed by a single digit\r
1852             int digIndx = 1;\r
1853             for (;;) {\r
1854                 // Make sure we have enough space.\r
1855                 if (digIndx >= ((m_utilStringBuffer_.length() - 2) << 1)) {\r
1856                     m_utilStringBuffer_.setLength(m_utilStringBuffer_.length() \r
1857                                                   << 1);\r
1858                 }\r
1859                 // Skipping over leading zeroes.        \r
1860                 if (digVal != 0 || nonZeroValReached) {\r
1861                     if (digVal != 0 && !nonZeroValReached) {\r
1862                         nonZeroValReached = true;\r
1863                     }    \r
1864                     // We parse the digit string into base 100 numbers \r
1865                     // (this fits into a byte).\r
1866                     // We only add to the buffer in twos, thus if we are \r
1867                     // parsing an odd character, that serves as the \r
1868                     // 'tens' digit while the if we are parsing an even \r
1869                     // one, that is the 'ones' digit. We dumped the \r
1870                     // parsed base 100 value (collateVal) into a buffer. \r
1871                     // We multiply each collateVal by 2 (to give us room) \r
1872                     // and add 5 (to avoid overlapping magic CE byte \r
1873                     // values). The last byte we subtract 1 to ensure it is \r
1874                     // less than all the other bytes.\r
1875                     if (digIndx % 2 == 1) {\r
1876                         collateVal += digVal;  \r
1877                         // This removes trailing zeroes.\r
1878                         if (collateVal == 0 && trailingZeroIndex == 0) {\r
1879                             trailingZeroIndex = ((digIndx - 1) >>> 1) + 2;\r
1880                         }\r
1881                         else if (trailingZeroIndex != 0) {\r
1882                             trailingZeroIndex = 0;\r
1883                         }\r
1884                         m_utilStringBuffer_.setCharAt(\r
1885                                             ((digIndx - 1) >>> 1) + 2,\r
1886                                             (char)((collateVal << 1) + 6));\r
1887                         collateVal = 0;\r
1888                     }\r
1889                     else {\r
1890                         // We drop the collation value into the buffer so if \r
1891                         // we need to do a "front patch" we don't have to \r
1892                         // check to see if we're hitting the last element.\r
1893                         collateVal = digVal * 10;\r
1894                         m_utilStringBuffer_.setCharAt((digIndx >>> 1) + 2, \r
1895                                                 (char)((collateVal << 1) + 6));\r
1896                     }\r
1897                     digIndx ++;\r
1898                 }\r
1899             \r
1900                 // Get next character.\r
1901                 if (!isEnd()){\r
1902                     backupInternalState(m_utilSpecialBackUp_);\r
1903                     int char32 = nextChar();\r
1904                     char ch = (char)char32;\r
1905                     if (UTF16.isLeadSurrogate(ch)){\r
1906                         if (!isEnd()) {\r
1907                             char trail = (char)nextChar();\r
1908                             if (UTF16.isTrailSurrogate(trail)) {\r
1909                                char32 = UCharacterProperty.getRawSupplementary(\r
1910                                                                    ch, trail);\r
1911                             } \r
1912                             else {\r
1913                                 goBackOne();\r
1914                             }\r
1915                         }\r
1916                     }\r
1917                     \r
1918                     digVal = UCharacter.digit(char32);\r
1919                     if (digVal == -1) {\r
1920                         // Resetting position to point to the next unprocessed \r
1921                         // char. We overshot it when doing our test/set for \r
1922                         // numbers.\r
1923                         updateInternalState(m_utilSpecialBackUp_);\r
1924                         break;\r
1925                     }\r
1926                 } \r
1927                 else {\r
1928                     break;\r
1929                 }\r
1930             }\r
1931         \r
1932             if (nonZeroValReached == false){\r
1933                 digIndx = 2;\r
1934                 m_utilStringBuffer_.setCharAt(2, (char)6);\r
1935             }\r
1936         \r
1937             int endIndex = trailingZeroIndex != 0 ? trailingZeroIndex \r
1938                                              : (digIndx >>> 1) + 2;              \r
1939             if (digIndx % 2 != 0){\r
1940                 // We missed a value. Since digIndx isn't even, stuck too many \r
1941                 // values into the buffer (this is what we get for padding the \r
1942                 // first byte with a zero). "Front-patch" now by pushing all \r
1943                 // nybbles forward.\r
1944                 // Doing it this way ensures that at least 50% of the time \r
1945                 // (statistically speaking) we'll only be doing a single pass \r
1946                 // and optimizes for strings with single digits. I'm just \r
1947                 // assuming that's the more common case.\r
1948                 for (int i = 2; i < endIndex; i ++){\r
1949                     m_utilStringBuffer_.setCharAt(i, \r
1950                         (char)((((((m_utilStringBuffer_.charAt(i) - 6) >>> 1) \r
1951                                   % 10) * 10) \r
1952                                  + (((m_utilStringBuffer_.charAt(i + 1) - 6) \r
1953                                       >>> 1) / 10) << 1) + 6));\r
1954                 }\r
1955                 -- digIndx;\r
1956             }\r
1957         \r
1958             // Subtract one off of the last byte. \r
1959             m_utilStringBuffer_.setCharAt(endIndex - 1, \r
1960                          (char)(m_utilStringBuffer_.charAt(endIndex - 1) - 1));            \r
1961                 \r
1962             // We want to skip over the first two slots in the buffer. \r
1963             // The first slot is reserved for the header byte CODAN_PLACEHOLDER. \r
1964             // The second slot is for the sign/exponent byte: \r
1965             // 0x80 + (decimalPos/2) & 7f.\r
1966             m_utilStringBuffer_.setCharAt(0, (char)RuleBasedCollator.CODAN_PLACEHOLDER);\r
1967             m_utilStringBuffer_.setCharAt(1, \r
1968                                      (char)(0x80 + ((digIndx >>> 1) & 0x7F)));\r
1969         \r
1970             // Now transfer the collation key to our collIterate struct.\r
1971             // The total size for our collation key is endIndx bumped up to the next largest even value divided by two.\r
1972             ce = (((m_utilStringBuffer_.charAt(0) << 8)\r
1973                        // Primary weight \r
1974                        | m_utilStringBuffer_.charAt(1)) \r
1975                                     << RuleBasedCollator.CE_PRIMARY_SHIFT_)\r
1976                        //  Secondary weight \r
1977                        | (RuleBasedCollator.BYTE_COMMON_ \r
1978                           << RuleBasedCollator.CE_SECONDARY_SHIFT_) \r
1979                        | RuleBasedCollator.BYTE_COMMON_; // Tertiary weight.\r
1980             int i = 2; // Reset the index into the buffer.\r
1981             \r
1982             m_CEBuffer_[0] = ce;\r
1983             m_CEBufferSize_ = 1;\r
1984             m_CEBufferOffset_ = 1;\r
1985             while (i < endIndex)\r
1986             {\r
1987                 int primWeight = m_utilStringBuffer_.charAt(i ++) << 8;\r
1988                 if (i < endIndex) {\r
1989                     primWeight |= m_utilStringBuffer_.charAt(i ++);\r
1990                 }\r
1991                 m_CEBuffer_[m_CEBufferSize_ ++] \r
1992                     = (primWeight << RuleBasedCollator.CE_PRIMARY_SHIFT_) \r
1993                       | RuleBasedCollator.CE_CONTINUATION_MARKER_;\r
1994             }\r
1995             return ce;\r
1996         } \r
1997         \r
1998         // no numeric mode, we'll just switch to whatever we stashed and \r
1999         // continue\r
2000         // find the offset to expansion table\r
2001         return collator.m_expansion_[getExpansionOffset(collator, ce)];\r
2002     }\r
2003 \r
2004     /**\r
2005      * Gets the next implicit ce for codepoints\r
2006      * @param codepoint current codepoint\r
2007      * @return implicit ce\r
2008      */\r
2009     private int nextImplicit(int codepoint)\r
2010     {\r
2011         if (!UCharacter.isLegal(codepoint)) {\r
2012             // synwee to check with vladimir on the range of isNonChar()\r
2013             // illegal code value, use completely ignoreable!\r
2014             return IGNORABLE;\r
2015         }\r
2016         int result = RuleBasedCollator.impCEGen_.getImplicitFromCodePoint(codepoint);\r
2017         m_CEBuffer_[0] = (result & RuleBasedCollator.CE_PRIMARY_MASK_)\r
2018                          | 0x00000505;\r
2019         m_CEBuffer_[1] = ((result & 0x0000FFFF) << 16) | 0x000000C0;\r
2020         m_CEBufferOffset_ = 1;\r
2021         m_CEBufferSize_ = 2;\r
2022         return m_CEBuffer_[0];\r
2023     }\r
2024 \r
2025     /**\r
2026      * Returns the next ce associated with the following surrogate characters\r
2027      * @param ch current character\r
2028      * @return ce\r
2029      */\r
2030     private int nextSurrogate(char ch)\r
2031     {\r
2032         int ch_int = nextChar();\r
2033         char nextch = (char)ch_int;\r
2034         if (ch_int != CharacterIterator.DONE &&\r
2035             UTF16.isTrailSurrogate(nextch)) {\r
2036             int codepoint = UCharacterProperty.getRawSupplementary(ch, nextch);\r
2037             return nextImplicit(codepoint);\r
2038         }\r
2039         if (nextch != CharacterIterator.DONE) {\r
2040             previousChar(); // reverts back to the original position\r
2041         }\r
2042         return IGNORABLE; // completely ignorable\r
2043     }\r
2044 \r
2045     /**\r
2046      * Returns the next ce for a hangul character, this is an implicit\r
2047      * calculation\r
2048      * @param collator current collator\r
2049      * @param ch current character\r
2050      * @return hangul ce\r
2051      */\r
2052     private int nextHangul(RuleBasedCollator collator, char ch)\r
2053     {\r
2054         char L = (char)(ch - HANGUL_SBASE_);\r
2055 \r
2056         // divide into pieces\r
2057         // do it in this order since some compilers can do % and / in one\r
2058         // operation\r
2059         char T = (char)(L % HANGUL_TCOUNT_);\r
2060         L /= HANGUL_TCOUNT_;\r
2061         char V = (char)(L % HANGUL_VCOUNT_);\r
2062         L /= HANGUL_VCOUNT_;\r
2063 \r
2064         // offset them\r
2065         L += HANGUL_LBASE_;\r
2066         V += HANGUL_VBASE_;\r
2067         T += HANGUL_TBASE_;\r
2068 \r
2069         // return the first CE, but first put the rest into the expansion\r
2070         // buffer\r
2071         m_CEBufferSize_ = 0;\r
2072         if (!collator.m_isJamoSpecial_) { // FAST PATH\r
2073             m_CEBuffer_[m_CEBufferSize_ ++] =\r
2074                 collator.m_trie_.getLeadValue(L);\r
2075             m_CEBuffer_[m_CEBufferSize_ ++] =\r
2076                 collator.m_trie_.getLeadValue(V);\r
2077 \r
2078             if (T != HANGUL_TBASE_) {\r
2079                 m_CEBuffer_[m_CEBufferSize_ ++] =\r
2080                     collator.m_trie_.getLeadValue(T);\r
2081             }\r
2082             m_CEBufferOffset_ = 1;\r
2083             return m_CEBuffer_[0];\r
2084         }\r
2085         else {\r
2086             // Jamo is Special\r
2087             // Since Hanguls pass the FCD check, it is guaranteed that we\r
2088             // won't be in the normalization buffer if something like this\r
2089             // happens\r
2090             // Move Jamos into normalization buffer\r
2091             m_buffer_.append((char)L);\r
2092             m_buffer_.append((char)V);\r
2093             if (T != HANGUL_TBASE_) {\r
2094                 m_buffer_.append((char)T);\r
2095             }\r
2096             m_FCDLimit_ = m_source_.getIndex();\r
2097             m_FCDStart_ = m_FCDLimit_ - 1;\r
2098             // Indicate where to continue in main input string after\r
2099             // exhausting the buffer\r
2100             return IGNORABLE;\r
2101         }\r
2102     }\r
2103 \r
2104     /**\r
2105      * <p>Special CE management. Expansions, contractions etc...</p>\r
2106      * @param collator can be plain UCA\r
2107      * @param ce current ce\r
2108      * @param ch current character\r
2109      * @return next special ce\r
2110      */\r
2111     private int nextSpecial(RuleBasedCollator collator, int ce, char ch)\r
2112     {\r
2113         int codepoint = ch;\r
2114         Backup entrybackup = m_utilSpecialEntryBackUp_;\r
2115         // this is to handle recursive looping\r
2116         if (entrybackup != null) {\r
2117             m_utilSpecialEntryBackUp_ = null;\r
2118         }\r
2119         else {\r
2120             entrybackup = new Backup();\r
2121         }\r
2122         backupInternalState(entrybackup);\r
2123         try { // forces it to assign m_utilSpecialEntryBackup_\r
2124             while (true) {\r
2125                 // This loop will repeat only in the case of contractions,\r
2126                 // surrogate\r
2127                 switch(RuleBasedCollator.getTag(ce)) {\r
2128                 case CE_NOT_FOUND_TAG_:\r
2129                     // impossible case for icu4j\r
2130                     return ce;\r
2131                 case RuleBasedCollator.CE_SURROGATE_TAG_:\r
2132                     if (isEnd()) {\r
2133                         return IGNORABLE;\r
2134                     }\r
2135                     backupInternalState(m_utilSpecialBackUp_);\r
2136                     char trail = (char)nextChar();\r
2137                     ce = nextSurrogate(collator, ce, trail);\r
2138                     // calculate the supplementary code point value,\r
2139                     // if surrogate was not tailored we go one more round\r
2140                     codepoint =\r
2141                         UCharacterProperty.getRawSupplementary(ch, trail);\r
2142                     break;\r
2143                 case CE_SPEC_PROC_TAG_:\r
2144                     ce = nextSpecialPrefix(collator, ce, entrybackup);\r
2145                     break;\r
2146                 case CE_CONTRACTION_TAG_:\r
2147                     ce = nextContraction(collator, ce);\r
2148                     break;\r
2149                 case CE_LONG_PRIMARY_TAG_:\r
2150                     return nextLongPrimary(ce);\r
2151                 case CE_EXPANSION_TAG_:\r
2152                     return nextExpansion(collator, ce);\r
2153                 case CE_DIGIT_TAG_:\r
2154                     ce = nextDigit(collator, ce, codepoint);\r
2155                     break;\r
2156                     // various implicits optimization\r
2157                 case CE_CJK_IMPLICIT_TAG_:\r
2158                     // 0x3400-0x4DB5, 0x4E00-0x9FA5, 0xF900-0xFA2D\r
2159                     return nextImplicit(codepoint);\r
2160                 case CE_IMPLICIT_TAG_: // everything that is not defined\r
2161                     return nextImplicit(codepoint);\r
2162                 case CE_TRAIL_SURROGATE_TAG_:\r
2163                     return IGNORABLE; // DC00-DFFF broken surrogate\r
2164                 case CE_LEAD_SURROGATE_TAG_:  // D800-DBFF\r
2165                     return nextSurrogate(ch);\r
2166                 case CE_HANGUL_SYLLABLE_TAG_: // AC00-D7AF\r
2167                     return nextHangul(collator, ch);\r
2168                 case CE_CHARSET_TAG_:\r
2169                                     // not yet implemented probably after 1.8\r
2170                     return CE_NOT_FOUND_;\r
2171                 default:\r
2172                     ce = IGNORABLE;\r
2173                     // synwee todo, throw exception or something here.\r
2174                 }\r
2175                 if (!RuleBasedCollator.isSpecial(ce)) {\r
2176                     break;\r
2177                 }\r
2178             }\r
2179         } \r
2180         finally {\r
2181             m_utilSpecialEntryBackUp_ = entrybackup;\r
2182         }\r
2183         return ce;\r
2184     }\r
2185 \r
2186     /**\r
2187      * Special processing is getting a CE that is preceded by a certain prefix.\r
2188      * Currently this is only needed for optimizing Japanese length and\r
2189      * iteration marks. When we encouter a special processing tag, we go\r
2190      * backwards and try to see if we have a match. Contraction tables are used\r
2191      * - so the whole process is not unlike contraction. prefix data is stored\r
2192      * backwards in the table.\r
2193      * @param collator current collator\r
2194      * @param ce current ce\r
2195      * @return previous ce\r
2196      */\r
2197     private int previousSpecialPrefix(RuleBasedCollator collator, int ce)\r
2198     {\r
2199         backupInternalState(m_utilSpecialBackUp_);\r
2200         while (true) {\r
2201             // position ourselves at the begining of contraction sequence\r
2202             int offset = getContractionOffset(collator, ce);\r
2203             int entryoffset = offset;\r
2204             if (isBackwardsStart()) {\r
2205                 ce = collator.m_contractionCE_[offset];\r
2206                 break;\r
2207             }\r
2208             char prevch = (char)previousChar();\r
2209             while (prevch > collator.m_contractionIndex_[offset]) {\r
2210                 // since contraction codepoints are ordered, we skip all that\r
2211                 // are smaller\r
2212                 offset ++;\r
2213             }\r
2214             if (prevch == collator.m_contractionIndex_[offset]) {\r
2215                 ce = collator.m_contractionCE_[offset];\r
2216             }\r
2217             else {\r
2218                 // if there is a completely ignorable code point in the middle\r
2219                 // of a prefix, we need to act as if it's not there assumption:\r
2220                 // 'real' noncharacters (*fffe, *ffff, fdd0-fdef are set to\r
2221                 // zero)\r
2222                 // lone surrogates cannot be set to zero as it would break\r
2223                 // other processing\r
2224                 int isZeroCE = collator.m_trie_.getLeadValue(prevch);\r
2225                 // it's easy for BMP code points\r
2226                 if (isZeroCE == 0) {\r
2227                     continue;\r
2228                 }\r
2229                 else if (UTF16.isTrailSurrogate(prevch)\r
2230                          || UTF16.isLeadSurrogate(prevch)) {\r
2231                     // for supplementary code points, we have to check the next one\r
2232                     // situations where we are going to ignore\r
2233                     // 1. beginning of the string: schar is a lone surrogate\r
2234                     // 2. schar is a lone surrogate\r
2235                     // 3. schar is a trail surrogate in a valid surrogate\r
2236                     //    sequence that is explicitly set to zero.\r
2237                     if (!isBackwardsStart()) {\r
2238                         char lead = (char)previousChar();\r
2239                         if (UTF16.isLeadSurrogate(lead)) {\r
2240                             isZeroCE = collator.m_trie_.getLeadValue(lead);\r
2241                             if (RuleBasedCollator.getTag(isZeroCE)\r
2242                                 == RuleBasedCollator.CE_SURROGATE_TAG_) {\r
2243                                 int finalCE = collator.m_trie_.getTrailValue(\r
2244                                                                       isZeroCE,\r
2245                                                                       prevch);\r
2246                                 if (finalCE == 0) {\r
2247                                     // this is a real, assigned completely\r
2248                                     // ignorable code point\r
2249                                     continue;\r
2250                                 }\r
2251                             }\r
2252                         }\r
2253                         else {\r
2254                             nextChar(); // revert to original offset\r
2255                             // lone surrogate, completely ignorable\r
2256                             continue;\r
2257                         }\r
2258                         nextChar(); // revert to original offset\r
2259                     }\r
2260                     else {\r
2261                          // lone surrogate at the beggining, completely ignorable\r
2262                          continue;\r
2263                     }\r
2264                 }\r
2265 \r
2266                 // char was not in the table. prefix not found\r
2267                 ce = collator.m_contractionCE_[entryoffset];\r
2268             }\r
2269 \r
2270             if (!isSpecialPrefixTag(ce)) {\r
2271                 // char was in the contraction table, and the corresponding ce\r
2272                 // is not a prefix ce.  We found the prefix, break out of loop,\r
2273                 // this ce will end up being returned.\r
2274                 break;\r
2275             }\r
2276         }\r
2277         updateInternalState(m_utilSpecialBackUp_);\r
2278         return ce;\r
2279     }\r
2280 \r
2281     /**\r
2282      * Retrieves the previous contraction ce. To ensure that the backwards and\r
2283      * forwards iteration matches, we take the current region of most possible\r
2284      * match and pass it through the forward iteration. This will ensure that\r
2285      * the obstinate problem of overlapping contractions will not occur.\r
2286      * @param collator current collator\r
2287      * @param ce current ce\r
2288      * @param ch current character\r
2289      * @return previous contraction ce\r
2290      */\r
2291     private int previousContraction(RuleBasedCollator collator, int ce, char ch)\r
2292     {\r
2293         m_utilStringBuffer_.setLength(0);\r
2294         // since we might encounter normalized characters (from the thai\r
2295         // processing) we can't use peekCharacter() here.\r
2296         char prevch = (char)previousChar();\r
2297         boolean atStart = false;\r
2298         // TODO: address the comment above - maybe now we *can* use peekCharacter\r
2299         //while (collator.isUnsafe(ch) || isThaiPreVowel(prevch)) {\r
2300         while (collator.isUnsafe(ch)) {\r
2301             m_utilStringBuffer_.insert(0, ch);\r
2302             ch = prevch;\r
2303             if (isBackwardsStart()) {\r
2304                 atStart = true;\r
2305                 break;\r
2306             }\r
2307             prevch = (char)previousChar();\r
2308         }\r
2309         if (!atStart) {\r
2310             // undo the previousChar() if we didn't reach the beginning \r
2311             nextChar();\r
2312         }\r
2313         // adds the initial base character to the string\r
2314         m_utilStringBuffer_.insert(0, ch);\r
2315 \r
2316         // a new collation element iterator is used to simply things, since\r
2317         // using the current collation element iterator will mean that the\r
2318         // forward and backwards iteration will share and change the same\r
2319         // buffers. it is going to be painful.\r
2320         int originaldecomp = collator.getDecomposition();\r
2321         // for faster access, since string would have been normalized above\r
2322         collator.setDecomposition(Collator.NO_DECOMPOSITION);\r
2323         if (m_utilColEIter_ == null) {\r
2324             m_utilColEIter_ = new CollationElementIterator(\r
2325                                                 m_utilStringBuffer_.toString(),\r
2326                                                 collator);\r
2327         }\r
2328         else {\r
2329             m_utilColEIter_.m_collator_ = collator;\r
2330             m_utilColEIter_.setText(m_utilStringBuffer_.toString());\r
2331         }\r
2332         ce = m_utilColEIter_.next();\r
2333         m_CEBufferSize_ = 0;\r
2334         while (ce != NULLORDER) {\r
2335             if (m_CEBufferSize_ == m_CEBuffer_.length) {\r
2336                 try {\r
2337                     // increasing cebuffer size\r
2338                     int tempbuffer[] = new int[m_CEBuffer_.length + 50];\r
2339                     System.arraycopy(m_CEBuffer_, 0, tempbuffer, 0,\r
2340                                      m_CEBuffer_.length);\r
2341                     m_CEBuffer_ = tempbuffer;\r
2342                 }\r
2343                 catch( MissingResourceException e)\r
2344                 {\r
2345                     throw e;\r
2346                 }\r
2347                 catch (Exception e) {\r
2348                     if(DEBUG){\r
2349                         e.printStackTrace();\r
2350                     }\r
2351                     return NULLORDER;\r
2352                 }\r
2353             }\r
2354             m_CEBuffer_[m_CEBufferSize_ ++] = ce;\r
2355             ce = m_utilColEIter_.next();\r
2356         }\r
2357         collator.setDecomposition(originaldecomp);\r
2358         m_CEBufferOffset_ = m_CEBufferSize_ - 1;\r
2359         return m_CEBuffer_[m_CEBufferOffset_];\r
2360     }\r
2361 \r
2362     /**\r
2363      * Returns the previous long primary ces\r
2364      * @param ce long primary ce\r
2365      * @return previous long primary ces\r
2366      */\r
2367     private int previousLongPrimary(int ce)\r
2368     {\r
2369         m_CEBufferSize_ = 0;\r
2370         m_CEBuffer_[m_CEBufferSize_ ++] =\r
2371             ((ce & 0xFFFF00) << 8) | (CE_BYTE_COMMON_ << 8) | CE_BYTE_COMMON_;\r
2372         m_CEBuffer_[m_CEBufferSize_ ++] = ((ce & 0xFF) << 24)\r
2373             | RuleBasedCollator.CE_CONTINUATION_MARKER_;\r
2374         m_CEBufferOffset_ = m_CEBufferSize_ - 1;\r
2375         return m_CEBuffer_[m_CEBufferOffset_];\r
2376     }\r
2377 \r
2378     /**\r
2379      * Returns the previous expansion ces\r
2380      * @param collator current collator\r
2381      * @param ce current ce\r
2382      * @return previous expansion ce\r
2383      */\r
2384     private int previousExpansion(RuleBasedCollator collator, int ce)\r
2385     {\r
2386         // find the offset to expansion table\r
2387         int offset = getExpansionOffset(collator, ce);\r
2388         m_CEBufferSize_ = getExpansionCount(ce);\r
2389         if (m_CEBufferSize_ != 0) {\r
2390             // less than 16 elements in expansion\r
2391             for (int i = 0; i < m_CEBufferSize_; i ++) {\r
2392                 m_CEBuffer_[i] = collator.m_expansion_[offset + i];\r
2393             }\r
2394 \r
2395         }\r
2396         else {\r
2397             // null terminated ces\r
2398             while (collator.m_expansion_[offset + m_CEBufferSize_] != 0) {\r
2399                 m_CEBuffer_[m_CEBufferSize_] =\r
2400                     collator.m_expansion_[offset + m_CEBufferSize_];\r
2401                 m_CEBufferSize_ ++;\r
2402             }\r
2403         }\r
2404         m_CEBufferOffset_ = m_CEBufferSize_ - 1;\r
2405         return m_CEBuffer_[m_CEBufferOffset_];\r
2406     }\r
2407     \r
2408     /**\r
2409      * Getting the digit collation elements\r
2410      * @param collator\r
2411      * @param ce current collation element\r
2412      * @param ch current code point\r
2413      * @return digit collation element\r
2414      */\r
2415     private int previousDigit(RuleBasedCollator collator, int ce, char ch)\r
2416     {\r
2417         // We do a check to see if we want to collate digits as numbers; if so we generate\r
2418         //  a custom collation key. Otherwise we pull out the value stored in the expansion table.\r
2419         if (m_collator_.m_isNumericCollation_){\r
2420             int leadingZeroIndex = 0;\r
2421             int collateVal = 0;\r
2422             boolean nonZeroValReached = false;\r
2423 \r
2424             // clear and set initial string buffer length\r
2425             m_utilStringBuffer_.setLength(3);\r
2426         \r
2427             // We parse the source string until we hit a char that's NOT a digit\r
2428             // Use this u_charDigitValue. This might be slow because we have to \r
2429             // handle surrogates...\r
2430             int char32 = ch;\r
2431             if (UTF16.isTrailSurrogate(ch)) {\r
2432                 if (!isBackwardsStart()){\r
2433                     char lead = (char)previousChar();\r
2434                     if (UTF16.isLeadSurrogate(lead)) {\r
2435                         char32 = UCharacterProperty.getRawSupplementary(lead,\r
2436                                                                         ch);\r
2437                     } \r
2438                     else {\r
2439                         goForwardOne();\r
2440                     }\r
2441                 }\r
2442             } \r
2443             int digVal = UCharacter.digit(char32);\r
2444             int digIndx = 0;\r
2445             for (;;) {\r
2446                 // Make sure we have enough space.\r
2447                 if (digIndx >= ((m_utilStringBuffer_.length() - 2) << 1)) {\r
2448                     m_utilStringBuffer_.setLength(m_utilStringBuffer_.length() \r
2449                                                   << 1);\r
2450                 }\r
2451                 // Skipping over "trailing" zeroes but we still add to digIndx.\r
2452                 if (digVal != 0 || nonZeroValReached) {\r
2453                     if (digVal != 0 && !nonZeroValReached) {\r
2454                         nonZeroValReached = true;\r
2455                     }\r
2456                 \r
2457                     // We parse the digit string into base 100 numbers (this \r
2458                     // fits into a byte).\r
2459                     // We only add to the buffer in twos, thus if we are \r
2460                     // parsing an odd character, that serves as the 'tens' \r
2461                     // digit while the if we are parsing an even one, that is \r
2462                     // the 'ones' digit. We dumped the parsed base 100 value \r
2463                     // (collateVal) into a buffer. We multiply each collateVal \r
2464                     // by 2 (to give us room) and add 5 (to avoid overlapping \r
2465                     // magic CE byte values). The last byte we subtract 1 to \r
2466                     // ensure it is less than all the other bytes. \r
2467                     // Since we're doing in this reverse we want to put the \r
2468                     // first digit encountered into the ones place and the \r
2469                     // second digit encountered into the tens place.\r
2470                 \r
2471                     if (digIndx % 2 == 1){\r
2472                         collateVal += digVal * 10;\r
2473                     \r
2474                         // This removes leading zeroes.\r
2475                         if (collateVal == 0 && leadingZeroIndex == 0) {\r
2476                            leadingZeroIndex = ((digIndx - 1) >>> 1) + 2;\r
2477                         }\r
2478                         else if (leadingZeroIndex != 0) {\r
2479                             leadingZeroIndex = 0;\r
2480                         }\r
2481                                             \r
2482                         m_utilStringBuffer_.setCharAt(((digIndx - 1) >>> 1) + 2, \r
2483                                                 (char)((collateVal << 1) + 6));\r
2484                         collateVal = 0;\r
2485                     }\r
2486                     else {\r
2487                         collateVal = digVal;    \r
2488                     }\r
2489                 }\r
2490                 digIndx ++;\r
2491             \r
2492                 if (!isBackwardsStart()){\r
2493                     backupInternalState(m_utilSpecialBackUp_);\r
2494                     char32 = previousChar();\r
2495                     ch = (char)ch;\r
2496                     if (UTF16.isTrailSurrogate(ch)){\r
2497                         if (!isBackwardsStart()) {\r
2498                             char lead = (char)previousChar();\r
2499                             if (UTF16.isLeadSurrogate(lead)) {\r
2500                                 char32 \r
2501                                     = UCharacterProperty.getRawSupplementary(\r
2502                                                                     lead, ch);\r
2503                             } \r
2504                             else {\r
2505                                 updateInternalState(m_utilSpecialBackUp_);\r
2506                             }\r
2507                         }\r
2508                     }\r
2509                     \r
2510                     digVal = UCharacter.digit(char32);\r
2511                     if (digVal == -1) {\r
2512                         updateInternalState(m_utilSpecialBackUp_);\r
2513                         break;\r
2514                     }\r
2515                 }\r
2516                 else {\r
2517                     break;\r
2518                 }\r
2519             }\r
2520 \r
2521             if (nonZeroValReached == false) {\r
2522                 digIndx = 2;\r
2523                 m_utilStringBuffer_.setCharAt(2, (char)6);\r
2524             }\r
2525             \r
2526             if (digIndx % 2 != 0) {\r
2527                 if (collateVal == 0 && leadingZeroIndex == 0) {\r
2528                     // This removes the leading 0 in a odd number sequence of \r
2529                     // numbers e.g. avery001\r
2530                     leadingZeroIndex = ((digIndx - 1) >>> 1) + 2;\r
2531                 }\r
2532                 else {\r
2533                     // this is not a leading 0, we add it in\r
2534                     m_utilStringBuffer_.setCharAt((digIndx >>> 1) + 2,\r
2535                                                 (char)((collateVal << 1) + 6));\r
2536                     digIndx ++; \r
2537                 }               \r
2538             }\r
2539                      \r
2540             int endIndex = leadingZeroIndex != 0 ? leadingZeroIndex \r
2541                                                : ((digIndx >>> 1) + 2) ;  \r
2542             digIndx = ((endIndex - 2) << 1) + 1; // removing initial zeros         \r
2543             // Subtract one off of the last byte. \r
2544             // Really the first byte here, but it's reversed...\r
2545             m_utilStringBuffer_.setCharAt(2, \r
2546                                     (char)(m_utilStringBuffer_.charAt(2) - 1));          \r
2547             // We want to skip over the first two slots in the buffer. \r
2548             // The first slot is reserved for the header byte CODAN_PLACEHOLDER. \r
2549             // The second slot is for the sign/exponent byte: \r
2550             // 0x80 + (decimalPos/2) & 7f.\r
2551             m_utilStringBuffer_.setCharAt(0, (char)RuleBasedCollator.CODAN_PLACEHOLDER);\r
2552             m_utilStringBuffer_.setCharAt(1, \r
2553                                     (char)(0x80 + ((digIndx >>> 1) & 0x7F)));\r
2554         \r
2555             // Now transfer the collation key to our collIterate struct.\r
2556             // The total size for our collation key is endIndx bumped up to the \r
2557             // next largest even value divided by two.\r
2558             m_CEBufferSize_ = 0;\r
2559             m_CEBuffer_[m_CEBufferSize_ ++] \r
2560                         = (((m_utilStringBuffer_.charAt(0) << 8)\r
2561                             // Primary weight \r
2562                             | m_utilStringBuffer_.charAt(1)) \r
2563                               << RuleBasedCollator.CE_PRIMARY_SHIFT_)\r
2564                             // Secondary weight \r
2565                             | (RuleBasedCollator.BYTE_COMMON_ \r
2566                                << RuleBasedCollator.CE_SECONDARY_SHIFT_)\r
2567                             // Tertiary weight. \r
2568                             | RuleBasedCollator.BYTE_COMMON_; \r
2569              int i = endIndex - 1; // Reset the index into the buffer.\r
2570              while (i >= 2) {\r
2571                 int primWeight = m_utilStringBuffer_.charAt(i --) << 8;\r
2572                 if (i >= 2) {\r
2573                     primWeight |= m_utilStringBuffer_.charAt(i --);\r
2574                 }\r
2575                 m_CEBuffer_[m_CEBufferSize_ ++] \r
2576                     = (primWeight << RuleBasedCollator.CE_PRIMARY_SHIFT_) \r
2577                       | RuleBasedCollator.CE_CONTINUATION_MARKER_;\r
2578              }\r
2579              m_CEBufferOffset_ = m_CEBufferSize_ - 1;\r
2580              return m_CEBuffer_[m_CEBufferOffset_];\r
2581          }\r
2582          else {\r
2583              return collator.m_expansion_[getExpansionOffset(collator, ce)];\r
2584          }\r
2585     } \r
2586 \r
2587     /**\r
2588      * Returns previous hangul ces\r
2589      * @param collator current collator\r
2590      * @param ch current character\r
2591      * @return previous hangul ce\r
2592      */\r
2593     private int previousHangul(RuleBasedCollator collator, char ch)\r
2594     {\r
2595         char L = (char)(ch - HANGUL_SBASE_);\r
2596         // we do it in this order since some compilers can do % and / in one\r
2597         // operation\r
2598         char T = (char)(L % HANGUL_TCOUNT_);\r
2599         L /= HANGUL_TCOUNT_;\r
2600         char V = (char)(L % HANGUL_VCOUNT_);\r
2601         L /= HANGUL_VCOUNT_;\r
2602 \r
2603         // offset them\r
2604         L += HANGUL_LBASE_;\r
2605         V += HANGUL_VBASE_;\r
2606         T += HANGUL_TBASE_;\r
2607 \r
2608         m_CEBufferSize_ = 0;\r
2609         if (!collator.m_isJamoSpecial_) {\r
2610             m_CEBuffer_[m_CEBufferSize_ ++] =\r
2611                 collator.m_trie_.getLeadValue(L);\r
2612             m_CEBuffer_[m_CEBufferSize_ ++] =\r
2613                 collator.m_trie_.getLeadValue(V);\r
2614             if (T != HANGUL_TBASE_) {\r
2615                 m_CEBuffer_[m_CEBufferSize_ ++] =\r
2616                     collator.m_trie_.getLeadValue(T);\r
2617             }\r
2618             m_CEBufferOffset_ = m_CEBufferSize_ - 1;\r
2619             return m_CEBuffer_[m_CEBufferOffset_];\r
2620         }\r
2621         else {\r
2622             // Since Hanguls pass the FCD check, it is guaranteed that we won't\r
2623             // be in the normalization buffer if something like this happens\r
2624             // Move Jamos into normalization buffer\r
2625             m_buffer_.append(L);\r
2626             m_buffer_.append(V);\r
2627             if (T != HANGUL_TBASE_) {\r
2628                 m_buffer_.append(T);\r
2629             }\r
2630 \r
2631             m_FCDStart_ = m_source_.getIndex();\r
2632             m_FCDLimit_ = m_FCDStart_ + 1;\r
2633             return IGNORABLE;\r
2634         }\r
2635     }\r
2636 \r
2637     /**\r
2638      * Gets implicit codepoint ces\r
2639      * @param codepoint current codepoint\r
2640      * @return implicit codepoint ces\r
2641      */\r
2642     private int previousImplicit(int codepoint)\r
2643     {\r
2644         if (!UCharacter.isLegal(codepoint)) {\r
2645             return IGNORABLE; // illegal code value, completely ignoreable!\r
2646         }\r
2647         int result = RuleBasedCollator.impCEGen_.getImplicitFromCodePoint(codepoint);\r
2648         m_CEBufferSize_ = 2;\r
2649         m_CEBufferOffset_ = 1;\r
2650         m_CEBuffer_[0] = (result & RuleBasedCollator.CE_PRIMARY_MASK_)\r
2651                          | 0x00000505;\r
2652         m_CEBuffer_[1] = ((result & 0x0000FFFF) << 16) | 0x000000C0;\r
2653         return m_CEBuffer_[1];\r
2654     }\r
2655 \r
2656     /**\r
2657      * Gets the previous surrogate ce\r
2658      * @param ch current character\r
2659      * @return previous surrogate ce\r
2660      */\r
2661     private int previousSurrogate(char ch)\r
2662     {\r
2663         if (isBackwardsStart()) {\r
2664             // we are at the start of the string, wrong place to be at\r
2665             return IGNORABLE;\r
2666         }\r
2667         char prevch = (char)previousChar();\r
2668         // Handles Han and Supplementary characters here.\r
2669         if (UTF16.isLeadSurrogate(prevch)) {\r
2670             return previousImplicit(\r
2671                           UCharacterProperty.getRawSupplementary(prevch, ch));\r
2672         }\r
2673         if (prevch != CharacterIterator.DONE) {\r
2674             nextChar();\r
2675         }\r
2676         return IGNORABLE; // completely ignorable\r
2677     }\r
2678 \r
2679     /**\r
2680      * <p>Special CE management. Expansions, contractions etc...</p>\r
2681      * @param collator can be plain UCA\r
2682      * @param ce current ce\r
2683      * @param ch current character\r
2684      * @return previous special ce\r
2685      */\r
2686     private int previousSpecial(RuleBasedCollator collator, int ce, char ch)\r
2687     {\r
2688         while(true) {\r
2689             // the only ces that loops are thai, special prefix and\r
2690             // contractions\r
2691             switch (RuleBasedCollator.getTag(ce)) {\r
2692             case CE_NOT_FOUND_TAG_:  // this tag always returns\r
2693                 return ce;\r
2694             case RuleBasedCollator.CE_SURROGATE_TAG_:\r
2695                                 // essentialy a disengaged lead surrogate. a broken\r
2696                                 // sequence was encountered and this is an error\r
2697                 return IGNORABLE;\r
2698             case CE_SPEC_PROC_TAG_:\r
2699                 ce = previousSpecialPrefix(collator, ce);\r
2700                 break;\r
2701             case CE_CONTRACTION_TAG_:\r
2702                 // may loop for first character e.g. "0x0f71" for english\r
2703                 if (isBackwardsStart()) {\r
2704                     // start of string or this is not the end of any contraction\r
2705                     ce = collator.m_contractionCE_[\r
2706                                             getContractionOffset(collator, ce)];\r
2707                     break;\r
2708                 }\r
2709                 return previousContraction(collator, ce, ch); // else\r
2710             case CE_LONG_PRIMARY_TAG_:\r
2711                 return previousLongPrimary(ce);\r
2712             case CE_EXPANSION_TAG_: // always returns\r
2713                 return previousExpansion(collator, ce);\r
2714             case CE_DIGIT_TAG_:\r
2715                 ce = previousDigit(collator, ce, ch);\r
2716                 break;\r
2717             case CE_HANGUL_SYLLABLE_TAG_: // AC00-D7AF\r
2718                 return previousHangul(collator, ch);\r
2719             case CE_LEAD_SURROGATE_TAG_:  // D800-DBFF\r
2720                 return IGNORABLE; // broken surrogate sequence\r
2721             case CE_TRAIL_SURROGATE_TAG_: // DC00-DFFF\r
2722                 return previousSurrogate(ch);\r
2723             case CE_CJK_IMPLICIT_TAG_:\r
2724                 // 0x3400-0x4DB5, 0x4E00-0x9FA5, 0xF900-0xFA2D\r
2725                 return previousImplicit(ch);\r
2726             case CE_IMPLICIT_TAG_: // everything that is not defined\r
2727                 // UCA is filled with these. Tailorings are NOT_FOUND\r
2728                 return previousImplicit(ch);\r
2729             case CE_CHARSET_TAG_: // this tag always returns\r
2730                 return CE_NOT_FOUND_;\r
2731             default: // this tag always returns\r
2732                 ce = IGNORABLE;\r
2733             }\r
2734             if (!RuleBasedCollator.isSpecial(ce)) {\r
2735                 break;\r
2736             }\r
2737         }\r
2738         return ce;\r
2739     }\r
2740 \r
2741     /**\r
2742      * GET IMPLICIT PRIMARY WEIGHTS\r
2743      * @param cp codepoint\r
2744      * @param value is left justified primary key\r
2745      */\r
2746 //    private static final int getImplicitPrimary(int cp)\r
2747 //    {\r
2748 //        cp = swapCJK(cp);\r
2749 //\r
2750 //        //if (DEBUG) System.out.println("CJK swapped: " + Utility.hex(cp));\r
2751 //        // we now have a range of numbers from 0 to 21FFFF.\r
2752 //        // we must skip all 00, 01, 02 bytes, so most bytes have 253 values\r
2753 //        // we must leave a gap of 01 between all values of the last byte, so\r
2754 //        // the last byte has 126 values (3 byte case)\r
2755 //        // we shift so that HAN all has the same first primary, for\r
2756 //        // compression.\r
2757 //        // for the 4 byte case, we make the gap as large as we can fit.\r
2758 //        // Three byte forms are EC xx xx, ED xx xx, EE xx xx (with a gap of 1)\r
2759 //        // Four byte forms (most supplementaries) are EF xx xx xx (with a gap\r
2760 //        // of LAST2_MULTIPLIER == 14)\r
2761 //\r
2762 //        int last0 = cp - RuleBasedCollator.IMPLICIT_4BYTE_BOUNDARY_;\r
2763 //        if (last0 < 0) {\r
2764 //            int last1 = cp / RuleBasedCollator.LAST_COUNT_;\r
2765 //            last0 = cp % RuleBasedCollator.LAST_COUNT_;\r
2766 //\r
2767 //            int last2 = last1 / RuleBasedCollator.OTHER_COUNT_;\r
2768 //            last1 %= RuleBasedCollator.OTHER_COUNT_;\r
2769 //            return RuleBasedCollator.IMPLICIT_BASE_3BYTE_ + (last2 << 24)\r
2770 //                   + (last1 << 16)\r
2771 //                   + ((last0 * RuleBasedCollator.LAST_MULTIPLIER_) << 8);\r
2772 //        }\r
2773 //        else {\r
2774 //            int last1 = last0 / RuleBasedCollator.LAST_COUNT2_;\r
2775 //            last0 %= RuleBasedCollator.LAST_COUNT2_;\r
2776 //\r
2777 //            int last2 = last1 / RuleBasedCollator.OTHER_COUNT_;\r
2778 //            last1 %= RuleBasedCollator.OTHER_COUNT_;\r
2779 //\r
2780 //            int last3 = last2 / RuleBasedCollator.OTHER_COUNT_;\r
2781 //            last2 %= RuleBasedCollator.OTHER_COUNT_;\r
2782 //            return RuleBasedCollator.IMPLICIT_BASE_4BYTE_ + (last3 << 24)\r
2783 //                   + (last2 << 16) + (last1 << 8)\r
2784 //                   + (last0 * RuleBasedCollator.LAST2_MULTIPLIER_);\r
2785 //        }\r
2786 //    }\r
2787 \r
2788 //    /**\r
2789 //     * Swapping CJK characters for implicit ces\r
2790 //     * @param cp codepoint CJK\r
2791 //     * @return swapped result\r
2792 //     */\r
2793 //    private static final int swapCJK(int cp)\r
2794 //    {\r
2795 //        if (cp >= CJK_BASE_) {\r
2796 //            if (cp < CJK_LIMIT_) {\r
2797 //                return cp - CJK_BASE_;\r
2798 //            }\r
2799 //            if (cp < CJK_COMPAT_USED_BASE_) {\r
2800 //                return cp + NON_CJK_OFFSET_;\r
2801 //            }\r
2802 //            if (cp < CJK_COMPAT_USED_LIMIT_) {\r
2803 //                return cp - CJK_COMPAT_USED_BASE_ + (CJK_LIMIT_ - CJK_BASE_);\r
2804 //            }\r
2805 //            if (cp < CJK_B_BASE_) {\r
2806 //                return cp + NON_CJK_OFFSET_;\r
2807 //            }\r
2808 //            if (cp < CJK_B_LIMIT_) {\r
2809 //                return cp; // non-BMP-CJK\r
2810 //            }\r
2811 //            return cp + NON_CJK_OFFSET_; // non-CJK\r
2812 //        }\r
2813 //        if (cp < CJK_A_BASE_) {\r
2814 //            return cp + NON_CJK_OFFSET_;\r
2815 //        }\r
2816 //        if (cp < CJK_A_LIMIT_) {\r
2817 //            return cp - CJK_A_BASE_ + (CJK_LIMIT_ - CJK_BASE_)\r
2818 //                   + (CJK_COMPAT_USED_LIMIT_ - CJK_COMPAT_USED_BASE_);\r
2819 //        }\r
2820 //        return cp + NON_CJK_OFFSET_; // non-CJK\r
2821 //    }\r
2822     \r
2823 //    /** \r
2824 //     * Gets a character from the source string at a given offset.\r
2825 //     * Handles both normal and iterative cases.\r
2826 //     * No error checking and does not access the normalization buffer \r
2827 //     * - caller beware!\r
2828 //     * @param offset offset from current position which character is to be \r
2829 //     *               retrieved\r
2830 //     * @return character at current position + offset\r
2831 //     */\r
2832 //    private char peekCharacter(int offset) \r
2833 //    {\r
2834 //        if (offset != 0) {\r
2835 //            int currentoffset = m_source_.getIndex();\r
2836 //            m_source_.setIndex(currentoffset + offset);\r
2837 //            char result = (char)m_source_.current();\r
2838 //            m_source_.setIndex(currentoffset);\r
2839 //            return result;\r
2840 //        } \r
2841 //        else {\r
2842 //            return (char)m_source_.current();\r
2843 //        }\r
2844 //    }\r
2845 \r
2846     /**\r
2847      * Moves back 1 position in the source string. This is slightly less \r
2848      * complicated than previousChar in that it doesn't normalize while \r
2849      * moving back. Boundary checks are not performed.\r
2850      * This method is to be used with caution, with the assumption that \r
2851      * moving back one position will not exceed the source limits.\r
2852      * Use only with nextChar() and never call this API twice in a row without\r
2853      * nextChar() in the middle.\r
2854      */\r
2855     private void goBackOne() \r
2856     {\r
2857         if (m_bufferOffset_ >= 0) {\r
2858             m_bufferOffset_ --;\r
2859         }\r
2860         else {\r
2861             m_source_.setIndex(m_source_.getIndex() - 1);\r
2862         }\r
2863     }\r
2864     \r
2865     /**\r
2866      * Moves forward 1 position in the source string. This is slightly less \r
2867      * complicated than nextChar in that it doesn't normalize while \r
2868      * moving back. Boundary checks are not performed.\r
2869      * This method is to be used with caution, with the assumption that \r
2870      * moving back one position will not exceed the source limits.\r
2871      * Use only with previousChar() and never call this API twice in a row \r
2872      * without previousChar() in the middle.\r
2873      */\r
2874     private void goForwardOne() \r
2875     {\r
2876         if (m_bufferOffset_ < 0) {\r
2877             // we're working on the source and not normalizing. fast path.\r
2878             // note Thai pre-vowel reordering uses buffer too\r
2879             m_source_.setIndex(m_source_.getIndex() + 1);\r
2880         }\r
2881         else {\r
2882             // we are in the buffer, buffer offset will never be 0 here\r
2883             m_bufferOffset_ ++;\r
2884         }\r
2885     }\r
2886 }\r