2 *******************************************************************************
\r
3 * Copyright (C) 1996-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
8 *******************************************************************************
\r
10 package com.ibm.icu.text;
\r
13 * import java.text.StringCharacterIterator;
\r
14 * import java.text.CharacterIterator;
\r
16 import java.text.CharacterIterator;
\r
17 import java.util.MissingResourceException;
\r
19 import com.ibm.icu.impl.CharacterIteratorWrapper;
\r
20 import com.ibm.icu.impl.ICUDebug;
\r
21 import com.ibm.icu.impl.Norm2AllModes;
\r
22 import com.ibm.icu.impl.Normalizer2Impl;
\r
23 import com.ibm.icu.impl.StringUCharacterIterator;
\r
24 import com.ibm.icu.impl.UCharacterProperty;
\r
25 import com.ibm.icu.lang.UCharacter;
\r
28 * <p><code>CollationElementIterator</code> is an iterator created by
\r
29 * a RuleBasedCollator to walk through a string. The return result of
\r
30 * each iteration is a 32-bit collation element that defines the
\r
31 * ordering priority of the next character or sequence of characters
\r
32 * in the source string.</p>
\r
34 * <p>For illustration, consider the following in Spanish:
\r
37 * "ca" -> the first collation element is collation_element('c') and second
\r
38 * collation element is collation_element('a').
\r
40 * Since "ch" in Spanish sorts as one entity, the below example returns one
\r
41 * collation element for the two characters 'c' and 'h'
\r
43 * "cha" -> the first collation element is collation_element('ch') and second
\r
44 * collation element is collation_element('a').
\r
50 * Since the character 'æ' is a composed character of 'a' and 'e', the
\r
51 * iterator returns two collation elements for the single character 'æ'
\r
53 * "æb" -> the first collation element is collation_element('a'), the
\r
54 * second collation element is collation_element('e'), and the
\r
55 * third collation element is collation_element('b').
\r
60 * <p>For collation ordering comparison, the collation element results
\r
61 * can not be compared simply by using basic arithmetric operators,
\r
62 * e.g. <, == or >, further processing has to be done. Details
\r
63 * can be found in the ICU
\r
64 * <a href="http://www.icu-project.org/userguide/Collate_ServiceArchitecture.html">
\r
65 * user guide</a>. An example of using the CollationElementIterator
\r
66 * for collation ordering comparison is the class
\r
67 * <a href=StringSearch.html> com.ibm.icu.text.StringSearch</a>.</p>
\r
69 * <p>To construct a CollationElementIterator object, users
\r
70 * call the method getCollationElementIterator() on a
\r
71 * RuleBasedCollator that defines the desired sorting order.</p>
\r
76 * String testString = "This is a test";
\r
77 * RuleBasedCollator rbc = new RuleBasedCollator("&a<b");
\r
78 * CollationElementIterator iterator = rbc.getCollationElementIterator(testString);
\r
79 * int primaryOrder = iterator.IGNORABLE;
\r
80 * while (primaryOrder != iterator.NULLORDER) {
\r
81 * int order = iterator.next();
\r
82 * if (order != iterator.IGNORABLE &&
\r
83 * order != iterator.NULLORDER) {
\r
84 * // order is valid, not ignorable and we have not passed the end
\r
85 * // of the iteration, we do something
\r
86 * primaryOrder = CollationElementIterator.primaryOrder(order);
\r
87 * System.out.println("Next primary order 0x" +
\r
88 * Integer.toHexString(primaryOrder));
\r
95 * This class is not subclassable
\r
98 * @see RuleBasedCollator
\r
100 * @author Syn Wee Quek
\r
103 public final class CollationElementIterator
\r
107 // public data members --------------------------------------------------
\r
110 * <p>This constant is returned by the iterator in the methods
\r
111 * next() and previous() when the end or the beginning of the
\r
112 * source string has been reached, and there are no more valid
\r
113 * collation elements to return.</p>
\r
115 * <p>See class documentation for an example of use.</p>
\r
118 * @see #previous */
\r
119 public final static int NULLORDER = 0xffffffff;
\r
122 * <p>This constant is returned by the iterator in the methods
\r
123 * next() and previous() when a collation element result is to be
\r
126 * <p>See class documentation for an example of use.</p>
\r
129 * @see #previous */
\r
130 public static final int IGNORABLE = 0;
\r
132 // public methods -------------------------------------------------------
\r
134 // public getters -------------------------------------------------------
\r
137 * <p>Returns the character offset in the source string
\r
138 * corresponding to the next collation element. I.e., getOffset()
\r
139 * returns the position in the source string corresponding to the
\r
140 * collation element that will be returned by the next call to
\r
141 * next(). This value could be any of:
\r
143 * <li> The index of the <b>first</b> character corresponding to
\r
144 * the next collation element. (This means that if
\r
145 * <code>setOffset(offset)</code> sets the index in the middle of
\r
146 * a contraction, <code>getOffset()</code> returns the index of
\r
147 * the first character in the contraction, which may not be equal
\r
148 * to the original offset that was set. Hence calling getOffset()
\r
149 * immediately after setOffset(offset) does not guarantee that the
\r
150 * original offset set will be returned.)
\r
151 * <li> If normalization is on, the index of the <b>immediate</b>
\r
152 * subsequent character, or composite character with the first
\r
153 * character, having a combining class of 0.
\r
154 * <li> The length of the source string, if iteration has reached
\r
158 * @return The character offset in the source string corresponding to the
\r
159 * collation element that will be returned by the next call to
\r
163 public int getOffset()
\r
165 if (m_bufferOffset_ != -1) {
\r
166 if (m_isForwards_) {
\r
167 return m_FCDLimit_;
\r
169 return m_FCDStart_;
\r
171 return m_source_.getIndex();
\r
176 * <p> Returns the maximum length of any expansion sequence that ends with
\r
177 * the specified collation element. If there is no expansion with this
\r
178 * collation element as the last element, returns 1.
\r
180 * @param ce a collation element returned by previous() or next().
\r
181 * @return the maximum length of any expansion sequence ending
\r
182 * with the specified collation element.
\r
185 public int getMaxExpansion(int ce)
\r
188 int limit = m_collator_.m_expansionEndCE_.length;
\r
189 long unsignedce = ce & 0xFFFFFFFFl;
\r
190 while (start < limit - 1) {
\r
191 int mid = start + ((limit - start) >> 1);
\r
192 long midce = m_collator_.m_expansionEndCE_[mid] & 0xFFFFFFFFl;
\r
193 if (unsignedce <= midce) {
\r
201 if (m_collator_.m_expansionEndCE_[start] == ce) {
\r
202 result = m_collator_.m_expansionEndCEMaxSize_[start];
\r
204 else if (limit < m_collator_.m_expansionEndCE_.length &&
\r
205 m_collator_.m_expansionEndCE_[limit] == ce) {
\r
206 result = m_collator_.m_expansionEndCEMaxSize_[limit];
\r
208 else if ((ce & 0xFFFF) == 0x00C0) {
\r
214 // public other methods -------------------------------------------------
\r
217 * <p> Resets the cursor to the beginning of the string. The next
\r
218 * call to next() or previous() will return the first and last
\r
219 * collation element in the string, respectively.</p>
\r
221 * <p>If the RuleBasedCollator used by this iterator has had its
\r
222 * attributes changed, calling reset() will reinitialize the
\r
223 * iterator to use the new attributes.</p>
\r
227 public void reset()
\r
229 m_source_.setToStart();
\r
230 updateInternalState();
\r
234 * <p>Get the next collation element in the source string.</p>
\r
236 * <p>This iterator iterates over a sequence of collation elements
\r
237 * that were built from the string. Because there isn't
\r
238 * necessarily a one-to-one mapping from characters to collation
\r
239 * elements, this doesn't mean the same thing as "return the
\r
240 * collation element [or ordering priority] of the next character
\r
241 * in the string".</p>
\r
243 * <p>This function returns the collation element that the
\r
244 * iterator is currently pointing to, and then updates the
\r
245 * internal pointer to point to the next element. Previous()
\r
246 * updates the pointer first, and then returns the element. This
\r
247 * means that when you change direction while iterating (i.e.,
\r
248 * call next() and then call previous(), or call previous() and
\r
249 * then call next()), you'll get back the same element twice.</p>
\r
251 * @return the next collation element or NULLORDER if the end of the
\r
252 * iteration has been reached.
\r
257 m_isForwards_ = true;
\r
258 if (m_CEBufferSize_ > 0) {
\r
259 if (m_CEBufferOffset_ < m_CEBufferSize_) {
\r
260 // if there are expansions left in the buffer, we return it
\r
261 return m_CEBuffer_[m_CEBufferOffset_ ++];
\r
263 m_CEBufferSize_ = 0;
\r
264 m_CEBufferOffset_ = 0;
\r
267 int ch_int = nextChar();
\r
269 if (ch_int == UCharacterIterator.DONE) {
\r
272 char ch = (char)ch_int;
\r
273 if (m_collator_.m_isHiragana4_) {
\r
274 /* Codepoints \u3099-\u309C are both Hiragana and Katakana. Set the flag
\r
275 * based on whether the previous codepoint was Hiragana or Katakana.
\r
277 m_isCodePointHiragana_ = (m_isCodePointHiragana_ && (ch >= 0x3099 && ch <= 0x309C)) ||
\r
278 ((ch >= 0x3040 && ch <= 0x309e) && !(ch > 0x3094 && ch < 0x309d));
\r
281 int result = NULLORDER;
\r
283 // For latin-1 characters we never need to fall back to the UCA
\r
284 // table because all of the UCA data is replicated in the
\r
285 // latinOneMapping array
\r
286 result = m_collator_.m_trie_.getLatin1LinearValue(ch);
\r
287 if (RuleBasedCollator.isSpecial(result)) {
\r
288 result = nextSpecial(m_collator_, result, ch);
\r
292 result = m_collator_.m_trie_.getLeadValue(ch);
\r
293 //System.out.println(Integer.toHexString(result));
\r
294 if (RuleBasedCollator.isSpecial(result)) {
\r
295 // surrogate leads are handled as special ces
\r
296 result = nextSpecial(m_collator_, result, ch);
\r
298 if (result == CE_NOT_FOUND_ && RuleBasedCollator.UCA_ != null) {
\r
299 // couldn't find a good CE in the tailoring
\r
300 // if we got here, the codepoint MUST be over 0xFF - so we look
\r
301 // directly in the UCA
\r
302 result = RuleBasedCollator.UCA_.m_trie_.getLeadValue(ch);
\r
303 if (RuleBasedCollator.isSpecial(result)) {
\r
304 // UCA also gives us a special CE
\r
305 result = nextSpecial(RuleBasedCollator.UCA_, result, ch);
\r
309 if(result == CE_NOT_FOUND_) {
\r
310 // maybe there is no UCA, unlikely in Java, but ported for consistency
\r
311 result = nextImplicit(ch);
\r
317 * <p>Get the previous collation element in the source string.</p>
\r
319 * <p>This iterator iterates over a sequence of collation elements
\r
320 * that were built from the string. Because there isn't
\r
321 * necessarily a one-to-one mapping from characters to collation
\r
322 * elements, this doesn't mean the same thing as "return the
\r
323 * collation element [or ordering priority] of the previous
\r
324 * character in the string".</p>
\r
326 * <p>This function updates the iterator's internal pointer to
\r
327 * point to the collation element preceding the one it's currently
\r
328 * pointing to and then returns that element, while next() returns
\r
329 * the current element and then updates the pointer. This means
\r
330 * that when you change direction while iterating (i.e., call
\r
331 * next() and then call previous(), or call previous() and then
\r
332 * call next()), you'll get back the same element twice.</p>
\r
334 * @return the previous collation element, or NULLORDER when the start of
\r
335 * the iteration has been reached.
\r
338 public int previous()
\r
340 if (m_source_.getIndex() <= 0 && m_isForwards_) {
\r
341 // if iterator is new or reset, we can immediate perform backwards
\r
342 // iteration even when the offset is not right.
\r
343 m_source_.setToLimit();
\r
344 updateInternalState();
\r
346 m_isForwards_ = false;
\r
347 int result = NULLORDER;
\r
348 if (m_CEBufferSize_ > 0) {
\r
349 if (m_CEBufferOffset_ > 0) {
\r
350 return m_CEBuffer_[-- m_CEBufferOffset_];
\r
352 m_CEBufferSize_ = 0;
\r
353 m_CEBufferOffset_ = 0;
\r
355 int ch_int = previousChar();
\r
356 if (ch_int == UCharacterIterator.DONE) {
\r
359 char ch = (char)ch_int;
\r
360 if (m_collator_.m_isHiragana4_) {
\r
361 m_isCodePointHiragana_ = (ch >= 0x3040 && ch <= 0x309f);
\r
363 if (m_collator_.isContractionEnd(ch) && !isBackwardsStart()) {
\r
364 result = previousSpecial(m_collator_, CE_CONTRACTION_, ch);
\r
368 result = m_collator_.m_trie_.getLatin1LinearValue(ch);
\r
371 result = m_collator_.m_trie_.getLeadValue(ch);
\r
373 if (RuleBasedCollator.isSpecial(result)) {
\r
374 result = previousSpecial(m_collator_, result, ch);
\r
376 if (result == CE_NOT_FOUND_) {
\r
377 if (!isBackwardsStart()
\r
378 && m_collator_.isContractionEnd(ch)) {
\r
379 result = CE_CONTRACTION_;
\r
382 if(RuleBasedCollator.UCA_ != null) {
\r
383 result = RuleBasedCollator.UCA_.m_trie_.getLeadValue(ch);
\r
387 if (RuleBasedCollator.isSpecial(result)) {
\r
388 if(RuleBasedCollator.UCA_ != null) {
\r
389 result = previousSpecial(RuleBasedCollator.UCA_, result, ch);
\r
394 if(result == CE_NOT_FOUND_) {
\r
395 result = previousImplicit(ch);
\r
401 * Return the primary order of the specified collation element,
\r
402 * i.e. the first 16 bits. This value is unsigned.
\r
403 * @param ce the collation element
\r
404 * @return the element's 16 bits primary order.
\r
407 public final static int primaryOrder(int ce)
\r
409 return (ce & RuleBasedCollator.CE_PRIMARY_MASK_)
\r
410 >>> RuleBasedCollator.CE_PRIMARY_SHIFT_;
\r
413 * Return the secondary order of the specified collation element,
\r
414 * i.e. the 16th to 23th bits, inclusive. This value is unsigned.
\r
415 * @param ce the collation element
\r
416 * @return the element's 8 bits secondary order
\r
419 public final static int secondaryOrder(int ce)
\r
421 return (ce & RuleBasedCollator.CE_SECONDARY_MASK_)
\r
422 >> RuleBasedCollator.CE_SECONDARY_SHIFT_;
\r
426 * Return the tertiary order of the specified collation element, i.e. the last
\r
427 * 8 bits. This value is unsigned.
\r
428 * @param ce the collation element
\r
429 * @return the element's 8 bits tertiary order
\r
432 public final static int tertiaryOrder(int ce)
\r
434 return ce & RuleBasedCollator.CE_TERTIARY_MASK_;
\r
438 * <p> Sets the iterator to point to the collation element
\r
439 * corresponding to the character at the specified offset. The
\r
440 * value returned by the next call to next() will be the collation
\r
441 * element corresponding to the characters at offset.</p>
\r
443 * <p>If offset is in the middle of a contracting character
\r
444 * sequence, the iterator is adjusted to the start of the
\r
445 * contracting sequence. This means that getOffset() is not
\r
446 * guaranteed to return the same value set by this method.</p>
\r
448 * <p>If the decomposition mode is on, and offset is in the middle
\r
449 * of a decomposible range of source text, the iterator may not
\r
450 * return a correct result for the next forwards or backwards
\r
451 * iteration. The user must ensure that the offset is not in the
\r
452 * middle of a decomposible range.</p>
\r
454 * @param offset the character offset into the original source string to
\r
455 * set. Note that this is not an offset into the corresponding
\r
456 * sequence of collation elements.
\r
459 public void setOffset(int offset)
\r
461 m_source_.setIndex(offset);
\r
462 int ch_int = m_source_.current();
\r
463 char ch = (char)ch_int;
\r
464 if (ch_int != UCharacterIterator.DONE && m_collator_.isUnsafe(ch)) {
\r
465 // if it is unsafe we need to check if it is part of a contraction
\r
466 // or a surrogate character
\r
467 if (UTF16.isTrailSurrogate(ch)) {
\r
468 // if it is a surrogate pair we move up one character
\r
469 char prevch = (char)m_source_.previous();
\r
470 if (!UTF16.isLeadSurrogate(prevch)) {
\r
471 m_source_.setIndex(offset); // go back to the same index
\r
475 // could be part of a contraction
\r
476 // backup to a safe point and iterate till we pass offset
\r
477 while (m_source_.getIndex() > 0) {
\r
478 if (!m_collator_.isUnsafe(ch)) {
\r
481 ch = (char)m_source_.previous();
\r
483 updateInternalState();
\r
484 int prevoffset = 0;
\r
485 while (m_source_.getIndex() <= offset) {
\r
486 prevoffset = m_source_.getIndex();
\r
489 m_source_.setIndex(prevoffset);
\r
492 updateInternalState();
\r
493 // direction code to prevent next and previous from returning a
\r
494 // character if we are already at the ends
\r
495 offset = m_source_.getIndex();
\r
496 if (offset == 0/* m_source_.getBeginIndex() */) {
\r
497 // preventing previous() from returning characters from the end of
\r
498 // the string again if we are at the beginning
\r
499 m_isForwards_ = false;
\r
501 else if (offset == m_source_.getLength()) {
\r
502 // preventing next() from returning characters from the start of
\r
503 // the string again if we are at the end
\r
504 m_isForwards_ = true;
\r
509 * <p>Set a new source string for iteration, and reset the offset
\r
510 * to the beginning of the text.</p>
\r
512 * @param source the new source string for iteration.
\r
515 public void setText(String source)
\r
517 m_srcUtilIter_.setText(source);
\r
518 m_source_ = m_srcUtilIter_;
\r
519 updateInternalState();
\r
523 * <p>Set a new source string iterator for iteration, and reset the
\r
524 * offset to the beginning of the text.
\r
526 * <p>The source iterator's integrity will be preserved since a new copy
\r
527 * will be created for use.</p>
\r
528 * @param source the new source string iterator for iteration.
\r
531 public void setText(UCharacterIterator source)
\r
533 m_srcUtilIter_.setText(source.getText());
\r
534 m_source_ = m_srcUtilIter_;
\r
535 updateInternalState();
\r
539 * <p>Set a new source string iterator for iteration, and reset the
\r
540 * offset to the beginning of the text.
\r
542 * @param source the new source string iterator for iteration.
\r
545 public void setText(CharacterIterator source)
\r
547 m_source_ = new CharacterIteratorWrapper(source);
\r
548 m_source_.setToStart();
\r
549 updateInternalState();
\r
552 // public miscellaneous methods -----------------------------------------
\r
555 * Tests that argument object is equals to this CollationElementIterator.
\r
556 * Iterators are equal if the objects uses the same RuleBasedCollator,
\r
557 * the same source text and have the same current position in iteration.
\r
558 * @param that object to test if it is equals to this
\r
559 * CollationElementIterator
\r
562 public boolean equals(Object that)
\r
564 if (that == this) {
\r
567 if (that instanceof CollationElementIterator) {
\r
568 CollationElementIterator thatceiter
\r
569 = (CollationElementIterator)that;
\r
570 if (!m_collator_.equals(thatceiter.m_collator_)) {
\r
573 // checks the text
\r
574 return m_source_.getIndex() == thatceiter.m_source_.getIndex()
\r
575 && m_source_.getText().equals(
\r
576 thatceiter.m_source_.getText());
\r
581 // package private constructors ------------------------------------------
\r
583 private CollationElementIterator(RuleBasedCollator collator) {
\r
584 m_utilStringBuffer_ = new StringBuilder();
\r
585 m_collator_ = collator;
\r
586 m_CEBuffer_ = new int[CE_BUFFER_INIT_SIZE_];
\r
587 m_buffer_ = new StringBuilder();
\r
588 m_utilSpecialBackUp_ = new Backup();
\r
589 m_nfcImpl_.getFCDTrie(); // ensure the FCD data is initialized
\r
593 * <p>CollationElementIterator constructor. This takes a source
\r
594 * string and a RuleBasedCollator. The iterator will walk through
\r
595 * the source string based on the rules defined by the
\r
596 * collator. If the source string is empty, NULLORDER will be
\r
597 * returned on the first call to next().</p>
\r
599 * @param source the source string.
\r
600 * @param collator the RuleBasedCollator
\r
603 CollationElementIterator(String source, RuleBasedCollator collator)
\r
606 m_source_ = m_srcUtilIter_ = new StringUCharacterIterator(source);
\r
607 updateInternalState();
\r
611 * <p>CollationElementIterator constructor. This takes a source
\r
612 * character iterator and a RuleBasedCollator. The iterator will
\r
613 * walk through the source string based on the rules defined by
\r
614 * the collator. If the source string is empty, NULLORDER will be
\r
615 * returned on the first call to next().</p>
\r
617 * @param source the source string iterator.
\r
618 * @param collator the RuleBasedCollator
\r
621 CollationElementIterator(CharacterIterator source,
\r
622 RuleBasedCollator collator)
\r
625 m_srcUtilIter_ = new StringUCharacterIterator();
\r
626 m_source_ = new CharacterIteratorWrapper(source);
\r
627 updateInternalState();
\r
631 * <p>CollationElementIterator constructor. This takes a source
\r
632 * character iterator and a RuleBasedCollator. The iterator will
\r
633 * walk through the source string based on the rules defined by
\r
634 * the collator. If the source string is empty, NULLORDER will be
\r
635 * returned on the first call to next().</p>
\r
637 * @param source the source string iterator.
\r
638 * @param collator the RuleBasedCollator
\r
641 CollationElementIterator(UCharacterIterator source,
\r
642 RuleBasedCollator collator)
\r
645 m_srcUtilIter_ = new StringUCharacterIterator();
\r
646 m_srcUtilIter_.setText(source.getText());
\r
647 m_source_ = m_srcUtilIter_;
\r
648 updateInternalState();
\r
651 // package private data members -----------------------------------------
\r
654 * true if current codepoint was Hiragana
\r
656 boolean m_isCodePointHiragana_;
\r
658 * Position in the original string that starts with a non-FCD sequence
\r
662 * This is the CE from CEs buffer that should be returned.
\r
663 * Initial value is 0.
\r
664 * Forwards iteration will end with m_CEBufferOffset_ == m_CEBufferSize_,
\r
665 * backwards will end with m_CEBufferOffset_ == 0.
\r
666 * The next/previous after we reach the end/beginning of the m_CEBuffer_
\r
667 * will cause this value to be reset to 0.
\r
669 int m_CEBufferOffset_;
\r
672 * This is the position to which we have stored processed CEs.
\r
673 * Initial value is 0.
\r
674 * The next/previous after we reach the end/beginning of the m_CEBuffer_
\r
675 * will cause this value to be reset to 0.
\r
677 int m_CEBufferSize_;
\r
678 static final int CE_NOT_FOUND_ = 0xF0000000;
\r
679 static final int CE_EXPANSION_TAG_ = 1;
\r
680 static final int CE_CONTRACTION_TAG_ = 2;
\r
682 * Collate Digits As Numbers (CODAN) implementation
\r
684 static final int CE_DIGIT_TAG_ = 13;
\r
686 // package private methods ----------------------------------------------
\r
689 * Sets the collator used.
\r
690 * Internal use, all data members will be reset to the default values
\r
691 * @param collator to set
\r
693 void setCollator(RuleBasedCollator collator)
\r
695 m_collator_ = collator;
\r
696 updateInternalState();
\r
700 * <p>Sets the iterator to point to the collation element corresponding to
\r
701 * the specified character (the parameter is a CHARACTER offset in the
\r
702 * original string, not an offset into its corresponding sequence of
\r
703 * collation elements). The value returned by the next call to next()
\r
704 * will be the collation element corresponding to the specified position
\r
705 * in the text. Unlike the public method setOffset(int), this method does
\r
706 * not try to readjust the offset to the start of a contracting sequence.
\r
707 * getOffset() is guaranteed to return the same value as was passed to a
\r
708 * preceding call to setOffset().</p>
\r
709 * @param offset new character offset into the original text to set.
\r
711 void setExactOffset(int offset)
\r
713 m_source_.setIndex(offset);
\r
714 updateInternalState();
\r
718 * Checks if iterator is in the buffer zone
\r
719 * @return true if iterator is in buffer zone, false otherwise
\r
721 boolean isInBuffer()
\r
723 return m_bufferOffset_ > 0;
\r
728 * <p>Sets the iterator to point to the collation element corresponding to
\r
729 * the specified character (the parameter is a CHARACTER offset in the
\r
730 * original string, not an offset into its corresponding sequence of
\r
731 * collation elements). The value returned by the next call to next()
\r
732 * will be the collation element corresponding to the specified position
\r
733 * in the text. Unlike the public method setOffset(int), this method does
\r
734 * not try to readjust the offset to the start of a contracting sequence.
\r
735 * getOffset() is guaranteed to return the same value as was passed to a
\r
736 * preceding call to setOffset().</p>
\r
738 * @param source the new source string iterator for iteration.
\r
739 * @param offset to the source
\r
741 void setText(UCharacterIterator source, int offset)
\r
743 m_srcUtilIter_.setText(source.getText());
\r
744 m_source_ = m_srcUtilIter_;
\r
745 m_source_.setIndex(offset);
\r
746 updateInternalState();
\r
749 // private inner class --------------------------------------------------
\r
752 * Backup data class
\r
754 private static final class Backup
\r
756 // protected data members -------------------------------------------
\r
759 * Backup non FCD sequence limit
\r
761 protected int m_FCDLimit_;
\r
763 * Backup non FCD sequence start
\r
765 protected int m_FCDStart_;
\r
767 * Backup if previous Codepoint is Hiragana quatenary
\r
769 protected boolean m_isCodePointHiragana_;
\r
771 * Backup buffer position
\r
773 protected int m_bufferOffset_;
\r
775 * Backup source iterator offset
\r
777 protected int m_offset_;
\r
779 * Backup buffer contents
\r
781 protected StringBuffer m_buffer_;
\r
783 // protected constructor --------------------------------------------
\r
786 * Empty constructor
\r
790 m_buffer_ = new StringBuffer();
\r
793 // end inner class ------------------------------------------------------
\r
796 * Direction of travel
\r
798 private boolean m_isForwards_;
\r
800 * Source string iterator
\r
802 private UCharacterIterator m_source_;
\r
804 * This is position to the m_buffer_, -1 if iterator is not in m_buffer_
\r
806 private int m_bufferOffset_;
\r
808 * Buffer for temporary storage of normalized characters, discontiguous
\r
809 * characters and Thai characters
\r
811 private StringBuilder m_buffer_;
\r
813 * Position in the original string to continue forward FCD check from.
\r
815 private int m_FCDLimit_;
\r
817 * The collator this iterator is based on
\r
819 private RuleBasedCollator m_collator_;
\r
821 * true if Hiragana quatenary is on
\r
823 //private boolean m_isHiragana4_;
\r
827 private int m_CEBuffer_[];
\r
829 * In reality we should not have to deal with expansion sequences longer
\r
830 * then 16. However this value can be change if a bigger buffer is needed.
\r
831 * Note, if the size is change to too small a number, BIG trouble.
\r
832 * Reasonable small value is around 10, if there's no Arabic or other
\r
833 * funky collations that have long expansion sequence. This is the longest
\r
834 * expansion sequence this can handle without bombing out.
\r
836 private static final int CE_BUFFER_INIT_SIZE_ = 512;
\r
838 * Backup storage for special processing inner cases
\r
840 private Backup m_utilSpecialBackUp_;
\r
842 * Backup storage in special processing entry state
\r
844 private Backup m_utilSpecialEntryBackUp_;
\r
846 * Backup storage in special processing discontiguous state
\r
848 private Backup m_utilSpecialDiscontiguousBackUp_;
\r
852 private StringUCharacterIterator m_srcUtilIter_;
\r
853 private StringBuilder m_utilStringBuffer_;
\r
854 private StringBuilder m_utilSkippedBuffer_;
\r
855 private CollationElementIterator m_utilColEIter_;
\r
856 private static final Normalizer2Impl m_nfcImpl_ = Norm2AllModes.getNFCInstance().impl;
\r
857 private StringBuilder m_unnormalized_;
\r
858 private Normalizer2Impl.ReorderingBuffer m_n2Buffer_;
\r
860 * The first non-zero combining class character
\r
862 private static final int FULL_ZERO_COMBINING_CLASS_FAST_LIMIT_ = 0xC0;
\r
864 * One character before the first character with leading non-zero combining
\r
867 private static final int LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_ = 0x300;
\r
869 * Mask for the last byte
\r
871 private static final int LAST_BYTE_MASK_ = 0xFF;
\r
873 * Shift value for the second last byte
\r
875 private static final int SECOND_LAST_BYTE_SHIFT_ = 8;
\r
877 // special ce values and tags -------------------------------------------
\r
879 // private static final int CE_EXPANSION_ = 0xF1000000;
\r
880 private static final int CE_CONTRACTION_ = 0xF2000000;
\r
882 * Indicates the last ce has been consumed. Compare with NULLORDER.
\r
883 * NULLORDER is returned if error occurs.
\r
885 /* private static final int CE_NO_MORE_CES_ = 0x00010101;
\r
886 private static final int CE_NO_MORE_CES_PRIMARY_ = 0x00010000;
\r
887 private static final int CE_NO_MORE_CES_SECONDARY_ = 0x00000100;
\r
888 private static final int CE_NO_MORE_CES_TERTIARY_ = 0x00000001;
\r
890 private static final int CE_NOT_FOUND_TAG_ = 0;
\r
892 * Charset processing, not yet implemented
\r
894 private static final int CE_CHARSET_TAG_ = 4;
\r
898 private static final int CE_HANGUL_SYLLABLE_TAG_ = 6;
\r
902 private static final int CE_LEAD_SURROGATE_TAG_ = 7;
\r
906 private static final int CE_TRAIL_SURROGATE_TAG_ = 8;
\r
908 * 0x3400-0x4DB5, 0x4E00-0x9FA5, 0xF900-0xFA2D
\r
910 private static final int CE_CJK_IMPLICIT_TAG_ = 9;
\r
911 private static final int CE_IMPLICIT_TAG_ = 10;
\r
912 static final int CE_SPEC_PROC_TAG_ = 11;
\r
914 * This is a 3 byte primary with starting secondaries and tertiaries.
\r
915 * It fits in a single 32 bit CE and is used instead of expansion to save
\r
916 * space without affecting the performance (hopefully).
\r
918 private static final int CE_LONG_PRIMARY_TAG_ = 12;
\r
920 // private static final int CE_CE_TAGS_COUNT = 14;
\r
921 private static final int CE_BYTE_COMMON_ = 0x05;
\r
923 // end special ce values and tags ---------------------------------------
\r
925 private static final int HANGUL_SBASE_ = 0xAC00;
\r
926 private static final int HANGUL_LBASE_ = 0x1100;
\r
927 private static final int HANGUL_VBASE_ = 0x1161;
\r
928 private static final int HANGUL_TBASE_ = 0x11A7;
\r
929 private static final int HANGUL_VCOUNT_ = 21;
\r
930 private static final int HANGUL_TCOUNT_ = 28;
\r
932 // CJK stuff ------------------------------------------------------------
\r
934 /* private static final int CJK_BASE_ = 0x4E00;
\r
935 private static final int CJK_LIMIT_ = 0x9FFF+1;
\r
936 private static final int CJK_COMPAT_USED_BASE_ = 0xFA0E;
\r
937 private static final int CJK_COMPAT_USED_LIMIT_ = 0xFA2F + 1;
\r
938 private static final int CJK_A_BASE_ = 0x3400;
\r
939 private static final int CJK_A_LIMIT_ = 0x4DBF + 1;
\r
940 private static final int CJK_B_BASE_ = 0x20000;
\r
941 private static final int CJK_B_LIMIT_ = 0x2A6DF + 1;
\r
942 private static final int NON_CJK_OFFSET_ = 0x110000;
\r
944 private static final boolean DEBUG = ICUDebug.enabled("collator");
\r
946 // private methods ------------------------------------------------------
\r
949 * Reset the iterator internally
\r
951 private void updateInternalState()
\r
953 m_isCodePointHiragana_ = false;
\r
954 m_buffer_.setLength(0);
\r
955 m_bufferOffset_ = -1;
\r
956 m_CEBufferOffset_ = 0;
\r
957 m_CEBufferSize_ = 0;
\r
959 m_FCDStart_ = m_source_.getLength();
\r
960 //m_isHiragana4_ = m_collator_.m_isHiragana4_;
\r
961 m_isForwards_ = true;
\r
965 * Backup the current internal state
\r
966 * @param backup object to store the data
\r
968 private void backupInternalState(Backup backup)
\r
970 backup.m_offset_ = m_source_.getIndex();
\r
971 backup.m_FCDLimit_ = m_FCDLimit_;
\r
972 backup.m_FCDStart_ = m_FCDStart_;
\r
973 backup.m_isCodePointHiragana_ = m_isCodePointHiragana_;
\r
974 backup.m_bufferOffset_ = m_bufferOffset_;
\r
975 backup.m_buffer_.setLength(0);
\r
976 if (m_bufferOffset_ >= 0) {
\r
977 backup.m_buffer_.append(m_buffer_);
\r
982 * Update the iterator internally with backed-up state
\r
983 * @param backup object that stored the data
\r
985 private void updateInternalState(Backup backup)
\r
987 m_source_.setIndex(backup.m_offset_);
\r
988 m_isCodePointHiragana_ = backup.m_isCodePointHiragana_;
\r
989 m_bufferOffset_ = backup.m_bufferOffset_;
\r
990 m_FCDLimit_ = backup.m_FCDLimit_;
\r
991 m_FCDStart_ = backup.m_FCDStart_;
\r
992 m_buffer_.setLength(0);
\r
993 if (m_bufferOffset_ >= 0) {
\r
994 m_buffer_.append(backup.m_buffer_);
\r
999 * A fast combining class retrieval system.
\r
1000 * @param ch UTF16 character
\r
1001 * @return combining class of ch
\r
1003 private int getCombiningClass(int ch)
\r
1005 if (ch >= LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_ &&
\r
1006 m_collator_.isUnsafe((char)ch) || ch > 0xFFFF
\r
1008 return m_nfcImpl_.getCC(m_nfcImpl_.getNorm16(ch));
\r
1014 * <p>Incremental normalization, this is an essential optimization.
\r
1015 * Assuming FCD checks has been done, normalize the non-FCD characters into
\r
1017 * Source offsets points to the current processing character.
\r
1020 private void normalize()
\r
1022 if (m_unnormalized_ == null) {
\r
1023 m_unnormalized_ = new StringBuilder();
\r
1024 m_n2Buffer_ = new Normalizer2Impl.ReorderingBuffer(m_nfcImpl_, m_buffer_, 10);
\r
1026 m_unnormalized_.setLength(0);
\r
1027 m_n2Buffer_.remove();
\r
1029 int size = m_FCDLimit_ - m_FCDStart_;
\r
1030 m_source_.setIndex(m_FCDStart_);
\r
1031 for (int i = 0; i < size; i ++) {
\r
1032 m_unnormalized_.append((char)m_source_.next());
\r
1034 m_nfcImpl_.decomposeShort(m_unnormalized_, 0, size, m_n2Buffer_);
\r
1038 * <p>Incremental FCD check and normalization. Gets the next base character
\r
1039 * position and determines if the in-between characters needs normalization.
\r
1041 * <p>When entering, the state is known to be this:
\r
1043 * <li>We are working on source string, not the buffer.
\r
1044 * <li>The leading combining class from the current character is 0 or the
\r
1045 * trailing combining class of the previous char was zero.
\r
1047 * Incoming source offsets points to the current processing character.
\r
1048 * Return source offsets points to the current processing character.
\r
1050 * @param ch current character (lead unit)
\r
1051 * @param offset offset of ch +1
\r
1052 * @return true if FCDCheck passes, false otherwise
\r
1054 private boolean FCDCheck(int ch, int offset)
\r
1056 boolean result = true;
\r
1058 // Get the trailing combining class of the current character.
\r
1059 // If it's zero, we are OK.
\r
1060 m_FCDStart_ = offset - 1;
\r
1061 m_source_.setIndex(offset);
\r
1063 int fcd = m_nfcImpl_.getFCD16FromSingleLead((char)ch);
\r
1064 if (fcd != 0 && Character.isHighSurrogate((char)ch)) {
\r
1065 int c2 = m_source_.next();
\r
1067 fcd = 0; // end of input
\r
1068 } else if (Character.isLowSurrogate((char)c2)) {
\r
1069 fcd = m_nfcImpl_.getFCD16(Character.toCodePoint((char)ch, (char)c2));
\r
1071 m_source_.moveIndex(-1);
\r
1076 int prevTrailCC = fcd & LAST_BYTE_MASK_;
\r
1078 if (prevTrailCC == 0) {
\r
1079 offset = m_source_.getIndex();
\r
1081 // The current char has a non-zero trailing CC. Scan forward until
\r
1082 // we find a char with a leading cc of zero.
\r
1084 ch = m_source_.nextCodePoint();
\r
1086 offset = m_source_.getIndex();
\r
1090 fcd = m_nfcImpl_.getFCD16(ch);
\r
1091 int leadCC = fcd >> SECOND_LAST_BYTE_SHIFT_;
\r
1092 if (leadCC == 0) {
\r
1093 // this is a base character, we stop the FCD checks
\r
1094 offset = m_source_.getIndex() - Character.charCount(ch);
\r
1098 if (leadCC < prevTrailCC) {
\r
1102 prevTrailCC = fcd & LAST_BYTE_MASK_;
\r
1105 m_FCDLimit_ = offset;
\r
1106 m_source_.setIndex(m_FCDStart_ + 1);
\r
1111 * <p>Method tries to fetch the next character that is in fcd form.</p>
\r
1112 * <p>Normalization is done if required.</p>
\r
1113 * <p>Offsets are returned at the next character.</p>
\r
1114 * @return next fcd character
\r
1116 private int nextChar()
\r
1120 // loop handles the next character whether it is in the buffer or not.
\r
1121 if (m_bufferOffset_ < 0) {
\r
1122 // we're working on the source and not normalizing. fast path.
\r
1123 // note Thai pre-vowel reordering uses buffer too
\r
1124 result = m_source_.next();
\r
1127 // we are in the buffer, buffer offset will never be 0 here
\r
1128 if (m_bufferOffset_ >= m_buffer_.length()) {
\r
1129 // Null marked end of buffer, revert to the source string and
\r
1130 // loop back to top to try again to get a character.
\r
1131 m_source_.setIndex(m_FCDLimit_);
\r
1132 m_bufferOffset_ = -1;
\r
1133 m_buffer_.setLength(0);
\r
1134 return nextChar();
\r
1136 return m_buffer_.charAt(m_bufferOffset_ ++);
\r
1138 int startoffset = m_source_.getIndex();
\r
1139 if (result < FULL_ZERO_COMBINING_CLASS_FAST_LIMIT_
\r
1140 // Fast fcd safe path. trail combining class == 0.
\r
1141 || m_collator_.getDecomposition() == Collator.NO_DECOMPOSITION
\r
1142 || m_bufferOffset_ >= 0 || m_FCDLimit_ >= startoffset) {
\r
1143 // skip the fcd checks
\r
1147 if (result < LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_) {
\r
1148 // We need to peek at the next character in order to tell if we are
\r
1150 int next = m_source_.current();
\r
1151 if (next == UCharacterIterator.DONE
\r
1152 || next < LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_) {
\r
1153 return result; // end of source string and if next character
\r
1154 // starts with a base character is always fcd.
\r
1158 // Need a more complete FCD check and possible normalization.
\r
1159 if (!FCDCheck(result, startoffset)) {
\r
1161 result = m_buffer_.charAt(0);
\r
1162 m_bufferOffset_ = 1;
\r
1168 * <p>Incremental normalization, this is an essential optimization.
\r
1169 * Assuming FCD checks has been done, normalize the non-FCD characters into
\r
1171 * Source offsets points to the current processing character.</p>
\r
1173 private void normalizeBackwards()
\r
1176 m_bufferOffset_ = m_buffer_.length();
\r
1180 * <p>Incremental backwards FCD check and normalization. Gets the previous
\r
1181 * base character position and determines if the in-between characters
\r
1182 * needs normalization.
\r
1184 * <p>When entering, the state is known to be this:
\r
1186 * <li>We are working on source string, not the buffer.
\r
1187 * <li>The trailing combining class from the current character is 0 or the
\r
1188 * leading combining class of the next char was zero.
\r
1190 * Input source offsets points to the previous character.
\r
1191 * Return source offsets points to the current processing character.
\r
1193 * @param ch current character
\r
1194 * @param offset current character offset
\r
1195 * @return true if FCDCheck passes, false otherwise
\r
1197 private boolean FCDCheckBackwards(int ch, int offset)
\r
1200 m_FCDLimit_ = offset + 1;
\r
1201 m_source_.setIndex(offset);
\r
1202 if (!UTF16.isSurrogate((char)ch)) {
\r
1203 fcd = m_nfcImpl_.getFCD16FromSingleLead((char)ch);
\r
1206 if (!Normalizer2Impl.UTF16Plus.isSurrogateLead(ch)) {
\r
1207 int c2 = m_source_.previous();
\r
1210 } else if (Character.isHighSurrogate((char)c2)) {
\r
1211 ch = Character.toCodePoint((char)c2, (char)ch);
\r
1212 fcd = m_nfcImpl_.getFCD16(ch);
\r
1215 m_source_.moveIndex(1);
\r
1220 // Scan backward until we find a char with a leading cc of zero.
\r
1221 boolean result = true;
\r
1225 leadCC = fcd >> SECOND_LAST_BYTE_SHIFT_;
\r
1226 if (leadCC == 0 || (ch = m_source_.previousCodePoint()) < 0) {
\r
1227 offset = m_source_.getIndex();
\r
1230 fcd = m_nfcImpl_.getFCD16(ch);
\r
1231 int prevTrailCC = fcd & LAST_BYTE_MASK_;
\r
1232 if (leadCC < prevTrailCC) {
\r
1234 } else if (fcd == 0) {
\r
1235 offset = m_source_.getIndex() + Character.charCount(ch);
\r
1241 // storing character with 0 lead fcd or the 1st accent with a base
\r
1242 // character before it
\r
1243 m_FCDStart_ = offset;
\r
1244 m_source_.setIndex(m_FCDLimit_);
\r
1249 * <p>Method tries to fetch the previous character that is in fcd form.</p>
\r
1250 * <p>Normalization is done if required.</p>
\r
1251 * <p>Offsets are returned at the current character.</p>
\r
1252 * @return previous fcd character
\r
1254 private int previousChar()
\r
1256 if (m_bufferOffset_ >= 0) {
\r
1257 m_bufferOffset_ --;
\r
1258 if (m_bufferOffset_ >= 0) {
\r
1259 return m_buffer_.charAt(m_bufferOffset_);
\r
1262 // At the start of buffer, route back to string.
\r
1263 m_buffer_.setLength(0);
\r
1264 if (m_FCDStart_ == 0) {
\r
1266 m_source_.setIndex(0);
\r
1267 return UCharacterIterator.DONE;
\r
1270 m_FCDLimit_ = m_FCDStart_;
\r
1271 m_source_.setIndex(m_FCDStart_);
\r
1272 return previousChar();
\r
1276 int result = m_source_.previous();
\r
1277 int startoffset = m_source_.getIndex();
\r
1278 if (result < LEAD_ZERO_COMBINING_CLASS_FAST_LIMIT_
\r
1279 || m_collator_.getDecomposition() == Collator.NO_DECOMPOSITION
\r
1280 || m_FCDStart_ <= startoffset || m_source_.getIndex() == 0) {
\r
1283 int ch = m_source_.previous();
\r
1284 if (ch < FULL_ZERO_COMBINING_CLASS_FAST_LIMIT_) {
\r
1285 // if previous character is FCD
\r
1289 // Need a more complete FCD check and possible normalization.
\r
1290 if (!FCDCheckBackwards(result, startoffset)) {
\r
1291 normalizeBackwards();
\r
1292 m_bufferOffset_ --;
\r
1293 result = m_buffer_.charAt(m_bufferOffset_);
\r
1296 // fcd checks always reset m_source_ to the limit of the FCD
\r
1297 m_source_.setIndex(startoffset);
\r
1303 * Determines if it is at the start of source iteration
\r
1304 * @return true if iterator at the start, false otherwise
\r
1306 private final boolean isBackwardsStart()
\r
1308 return (m_bufferOffset_ < 0 && m_source_.getIndex() == 0)
\r
1309 || (m_bufferOffset_ == 0 && m_FCDStart_ <= 0);
\r
1313 * Checks if iterator is at the end of its source string.
\r
1314 * @return true if it is at the end, false otherwise
\r
1316 private final boolean isEnd()
\r
1318 if (m_bufferOffset_ >= 0) {
\r
1319 if (m_bufferOffset_ != m_buffer_.length()) {
\r
1323 // at end of buffer. check if fcd is at the end
\r
1324 return m_FCDLimit_ == m_source_.getLength();
\r
1327 return m_source_.getLength() == m_source_.getIndex();
\r
1331 * <p>Special CE management for surrogates</p>
\r
1332 * <p>Lead surrogate is encountered. CE to be retrieved by using the
\r
1333 * following code unit. If next character is a trail surrogate, both
\r
1334 * characters will be combined to retrieve the CE, otherwise completely
\r
1335 * ignorable (UCA specification) is returned.</p>
\r
1336 * @param collator collator to use
\r
1337 * @param ce current CE
\r
1338 * @param trail character
\r
1339 * @return next CE for the surrogate characters
\r
1341 private final int nextSurrogate(RuleBasedCollator collator, int ce,
\r
1344 if (!UTF16.isTrailSurrogate(trail)) {
\r
1345 updateInternalState(m_utilSpecialBackUp_);
\r
1348 // TODO: CE contain the data from the previous CE + the mask.
\r
1349 // It should at least be unmasked
\r
1350 int result = collator.m_trie_.getTrailValue(ce, trail);
\r
1351 if (result == CE_NOT_FOUND_) {
\r
1352 updateInternalState(m_utilSpecialBackUp_);
\r
1358 * Gets the CE expansion offset
\r
1359 * @param collator current collator
\r
1360 * @param ce ce to test
\r
1361 * @return expansion offset
\r
1363 private int getExpansionOffset(RuleBasedCollator collator, int ce)
\r
1365 return ((ce & 0xFFFFF0) >> 4) - collator.m_expansionOffset_;
\r
1370 * Gets the contraction ce offset
\r
1371 * @param collator current collator
\r
1372 * @param ce current ce
\r
1373 * @return contraction offset
\r
1375 private int getContractionOffset(RuleBasedCollator collator, int ce)
\r
1377 return (ce & 0xFFFFFF) - collator.m_contractionOffset_;
\r
1381 * Checks if CE is a special tag CE
\r
1382 * @param ce to check
\r
1383 * @return true if CE is a special tag CE, false otherwise
\r
1385 private boolean isSpecialPrefixTag(int ce)
\r
1387 return RuleBasedCollator.isSpecial(ce) &&
\r
1388 RuleBasedCollator.getTag(ce) == CE_SPEC_PROC_TAG_;
\r
1392 * <p>Special processing getting a CE that is preceded by a certain
\r
1394 * <p>Used for optimizing Japanese length and iteration marks. When a
\r
1395 * special processing tag is encountered, iterate backwards to see if
\r
1396 * there's a match.</p>
\r
1397 * <p>Contraction tables are used, prefix data is stored backwards in the
\r
1399 * @param collator collator to use
\r
1400 * @param ce current ce
\r
1401 * @param entrybackup entry backup iterator status
\r
1402 * @return next collation element
\r
1404 private int nextSpecialPrefix(RuleBasedCollator collator, int ce,
\r
1405 Backup entrybackup)
\r
1407 backupInternalState(m_utilSpecialBackUp_);
\r
1408 updateInternalState(entrybackup);
\r
1410 // We want to look at the character where we entered
\r
1413 // This loop will run once per source string character, for as
\r
1414 // long as we are matching a potential contraction sequence
\r
1415 // First we position ourselves at the begining of contraction
\r
1417 int entryoffset = getContractionOffset(collator, ce);
\r
1418 int offset = entryoffset;
\r
1419 if (isBackwardsStart()) {
\r
1420 ce = collator.m_contractionCE_[offset];
\r
1423 char previous = (char)previousChar();
\r
1424 while (previous > collator.m_contractionIndex_[offset]) {
\r
1425 // contraction characters are ordered, skip smaller characters
\r
1429 if (previous == collator.m_contractionIndex_[offset]) {
\r
1430 // Found the source string char in the table.
\r
1431 // Pick up the corresponding CE from the table.
\r
1432 ce = collator.m_contractionCE_[offset];
\r
1435 // Source string char was not in the table, prefix not found
\r
1436 ce = collator.m_contractionCE_[entryoffset];
\r
1439 if (!isSpecialPrefixTag(ce)) {
\r
1440 // The source string char was in the contraction table, and
\r
1441 // the corresponding CE is not a prefix CE. We found the
\r
1442 // prefix, break out of loop, this CE will end up being
\r
1443 // returned. This is the normal way out of prefix handling
\r
1444 // when the source actually contained the prefix.
\r
1448 if (ce != CE_NOT_FOUND_) {
\r
1449 // we found something and we can merilly continue
\r
1450 updateInternalState(m_utilSpecialBackUp_);
\r
1452 else { // prefix search was a failure, we have to backup all the way to
\r
1454 updateInternalState(entrybackup);
\r
1460 * Checks if the ce is a contraction tag
\r
1461 * @param ce ce to check
\r
1462 * @return true if ce is a contraction tag, false otherwise
\r
1464 private boolean isContractionTag(int ce)
\r
1466 return RuleBasedCollator.isSpecial(ce) &&
\r
1467 RuleBasedCollator.getTag(ce) == CE_CONTRACTION_TAG_;
\r
1471 * Method to copy skipped characters into the buffer and sets the fcd
\r
1472 * position. To ensure that the skipped characters are considered later,
\r
1473 * we need to place it in the appropriate position in the buffer and
\r
1474 * reassign the source index. simple case if index reside in string,
\r
1475 * simply copy to buffer and fcdposition = pos, pos = start of buffer.
\r
1476 * if pos in normalization buffer, we'll insert the copy infront of pos
\r
1477 * and point pos to the start of the buffer. why am i doing these copies?
\r
1478 * well, so that the whole chunk of codes in the getNextCE,
\r
1479 * ucol_prv_getSpecialCE does not require any changes, which will be
\r
1481 * @param skipped character buffer
\r
1483 private void setDiscontiguous(StringBuilder skipped)
\r
1485 if (m_bufferOffset_ >= 0) {
\r
1486 m_buffer_.replace(0, m_bufferOffset_, skipped.toString());
\r
1489 m_FCDLimit_ = m_source_.getIndex();
\r
1490 m_buffer_.setLength(0);
\r
1491 m_buffer_.append(skipped.toString());
\r
1494 m_bufferOffset_ = 0;
\r
1498 * Returns the current character for forward iteration
\r
1499 * @return current character
\r
1501 private int currentChar()
\r
1503 if (m_bufferOffset_ < 0) {
\r
1504 m_source_.previous();
\r
1505 return m_source_.next();
\r
1508 // m_bufferOffset_ is never 0 in normal circumstances except after a
\r
1509 // discontiguous contraction since it is always returned and moved
\r
1510 // by 1 when we do nextChar()
\r
1511 return m_buffer_.charAt(m_bufferOffset_ - 1);
\r
1515 * Method to get the discontiguous collation element within the source.
\r
1516 * Note this function will set the position to the appropriate places.
\r
1517 * Passed in character offset points to the second combining character
\r
1518 * after the start character.
\r
1519 * @param collator current collator used
\r
1520 * @param entryoffset index to the start character in the contraction table
\r
1521 * @return discontiguous collation element offset
\r
1523 private int nextDiscontiguous(RuleBasedCollator collator, int entryoffset)
\r
1525 int offset = entryoffset;
\r
1526 boolean multicontraction = false;
\r
1527 // since it will be stuffed into this iterator and ran over again
\r
1528 if (m_utilSkippedBuffer_ == null) {
\r
1529 m_utilSkippedBuffer_ = new StringBuilder();
\r
1532 m_utilSkippedBuffer_.setLength(0);
\r
1534 char ch = (char)currentChar();
\r
1535 m_utilSkippedBuffer_.append((char)currentChar());
\r
1536 // accent after the first character
\r
1537 if (m_utilSpecialDiscontiguousBackUp_ == null) {
\r
1538 m_utilSpecialDiscontiguousBackUp_ = new Backup();
\r
1540 backupInternalState(m_utilSpecialDiscontiguousBackUp_);
\r
1544 int ch_int = nextChar();
\r
1545 nextch = (char)ch_int;
\r
1546 if (ch_int == UCharacterIterator.DONE
\r
1547 || getCombiningClass(nextch) == 0) {
\r
1548 // if there are no more accents to move around
\r
1549 // we don't have to shift previousChar, since we are resetting
\r
1550 // the offset later
\r
1551 if (multicontraction) {
\r
1552 if (ch_int != UCharacterIterator.DONE) {
\r
1553 previousChar(); // backtrack
\r
1555 setDiscontiguous(m_utilSkippedBuffer_);
\r
1556 return collator.m_contractionCE_[offset];
\r
1561 offset ++; // skip the combining class offset
\r
1562 while ((offset < collator.m_contractionIndex_.length) &&
\r
1563 (nextch > collator.m_contractionIndex_[offset])) {
\r
1567 int ce = CE_NOT_FOUND_;
\r
1568 if ( offset >= collator.m_contractionIndex_.length) {
\r
1571 if ( nextch != collator.m_contractionIndex_[offset]
\r
1572 || getCombiningClass(nextch) == getCombiningClass(ch)) {
\r
1573 // unmatched or blocked character
\r
1574 if ( (m_utilSkippedBuffer_.length()!= 1) ||
\r
1575 ((m_utilSkippedBuffer_.charAt(0)!= nextch) &&
\r
1576 (m_bufferOffset_<0) )) { // avoid push to skipped buffer twice
\r
1577 m_utilSkippedBuffer_.append(nextch);
\r
1579 offset = entryoffset; // Restore the offset before checking next character.
\r
1583 ce = collator.m_contractionCE_[offset];
\r
1586 if (ce == CE_NOT_FOUND_) {
\r
1589 else if (isContractionTag(ce)) {
\r
1590 // this is a multi-contraction
\r
1591 offset = getContractionOffset(collator, ce);
\r
1592 if (collator.m_contractionCE_[offset] != CE_NOT_FOUND_) {
\r
1593 multicontraction = true;
\r
1594 backupInternalState(m_utilSpecialDiscontiguousBackUp_);
\r
1598 setDiscontiguous(m_utilSkippedBuffer_);
\r
1603 updateInternalState(m_utilSpecialDiscontiguousBackUp_);
\r
1604 // backup is one forward of the base character, we need to move back
\r
1607 return collator.m_contractionCE_[entryoffset];
\r
1611 * Gets the next contraction ce
\r
1612 * @param collator collator to use
\r
1613 * @param ce current ce
\r
1614 * @return ce of the next contraction
\r
1616 private int nextContraction(RuleBasedCollator collator, int ce)
\r
1618 backupInternalState(m_utilSpecialBackUp_);
\r
1619 int entryce = collator.m_contractionCE_[getContractionOffset(collator, ce)]; //CE_NOT_FOUND_;
\r
1621 int entryoffset = getContractionOffset(collator, ce);
\r
1622 int offset = entryoffset;
\r
1625 ce = collator.m_contractionCE_[offset];
\r
1626 if (ce == CE_NOT_FOUND_) {
\r
1627 // back up the source over all the chars we scanned going
\r
1628 // into this contraction.
\r
1630 updateInternalState(m_utilSpecialBackUp_);
\r
1635 // get the discontiguos maximum combining class
\r
1636 int maxCC = (collator.m_contractionIndex_[offset] & 0xFF);
\r
1637 // checks if all characters have the same combining class
\r
1638 byte allSame = (byte)(collator.m_contractionIndex_[offset] >> 8);
\r
1639 char ch = (char)nextChar();
\r
1641 while (ch > collator.m_contractionIndex_[offset]) {
\r
1642 // contraction characters are ordered, skip all smaller
\r
1646 if (ch == collator.m_contractionIndex_[offset]) {
\r
1647 // Found the source string char in the contraction table.
\r
1648 // Pick up the corresponding CE from the table.
\r
1649 ce = collator.m_contractionCE_[offset];
\r
1652 // Source string char was not in contraction table.
\r
1653 // Unless it is a discontiguous contraction, we are done
\r
1655 if(UTF16.isLeadSurrogate(ch)) { // in order to do the proper detection, we
\r
1656 // need to see if we're dealing with a supplementary
\r
1657 miss = UCharacterProperty.getRawSupplementary(ch, (char) nextChar());
\r
1660 if (maxCC == 0 || (sCC = getCombiningClass(miss)) == 0
\r
1661 || sCC > maxCC || (allSame != 0 && sCC == maxCC) ||
\r
1663 // Contraction can not be discontiguous, back up by one
\r
1665 if(miss > 0xFFFF) {
\r
1668 ce = collator.m_contractionCE_[entryoffset];
\r
1671 // Contraction is possibly discontiguous.
\r
1672 // find the next character if ch is not a base character
\r
1673 int ch_int = nextChar();
\r
1674 if (ch_int != UCharacterIterator.DONE) {
\r
1677 char nextch = (char)ch_int;
\r
1678 if (getCombiningClass(nextch) == 0) {
\r
1680 if(miss > 0xFFFF) {
\r
1683 // base character not part of discontiguous contraction
\r
1684 ce = collator.m_contractionCE_[entryoffset];
\r
1687 ce = nextDiscontiguous(collator, entryoffset);
\r
1692 if (ce == CE_NOT_FOUND_) {
\r
1693 // source did not match the contraction, revert back original
\r
1694 updateInternalState(m_utilSpecialBackUp_);
\r
1699 // source was a contraction
\r
1700 if (!isContractionTag(ce)) {
\r
1704 // ccontinue looping to check for the remaining contraction.
\r
1705 if (collator.m_contractionCE_[entryoffset] != CE_NOT_FOUND_) {
\r
1706 // there are further contractions to be performed, so we store
\r
1707 // the so-far completed ce, so that if we fail in the next
\r
1708 // round we just return this one.
\r
1709 entryce = collator.m_contractionCE_[entryoffset];
\r
1710 backupInternalState(m_utilSpecialBackUp_);
\r
1711 if (m_utilSpecialBackUp_.m_bufferOffset_ >= 0) {
\r
1712 m_utilSpecialBackUp_.m_bufferOffset_ --;
\r
1715 m_utilSpecialBackUp_.m_offset_ --;
\r
1723 * Gets the next ce for long primaries, stuffs the rest of the collation
\r
1724 * elements into the ce buffer
\r
1725 * @param ce current ce
\r
1728 private int nextLongPrimary(int ce)
\r
1730 m_CEBuffer_[1] = ((ce & 0xFF) << 24)
\r
1731 | RuleBasedCollator.CE_CONTINUATION_MARKER_;
\r
1732 m_CEBufferOffset_ = 1;
\r
1733 m_CEBufferSize_ = 2;
\r
1734 m_CEBuffer_[0] = ((ce & 0xFFFF00) << 8) | (CE_BYTE_COMMON_ << 8) |
\r
1736 return m_CEBuffer_[0];
\r
1740 * Gets the number of expansion
\r
1741 * @param ce current ce
\r
1742 * @return number of expansion
\r
1744 private int getExpansionCount(int ce)
\r
1750 * Gets the next expansion ce and stuffs the rest of the collation elements
\r
1751 * into the ce buffer
\r
1752 * @param collator current collator
\r
1753 * @param ce current ce
\r
1754 * @return next expansion ce
\r
1756 private int nextExpansion(RuleBasedCollator collator, int ce)
\r
1758 // NOTE: we can encounter both continuations and expansions in an
\r
1760 // I have to decide where continuations are going to be dealt with
\r
1761 int offset = getExpansionOffset(collator, ce);
\r
1762 m_CEBufferSize_ = getExpansionCount(ce);
\r
1763 m_CEBufferOffset_ = 1;
\r
1764 m_CEBuffer_[0] = collator.m_expansion_[offset];
\r
1765 if (m_CEBufferSize_ != 0) {
\r
1766 // if there are less than 16 elements in expansion
\r
1767 for (int i = 1; i < m_CEBufferSize_; i ++) {
\r
1768 m_CEBuffer_[i] = collator.m_expansion_[offset + i];
\r
1772 // ce are terminated
\r
1773 m_CEBufferSize_ = 1;
\r
1774 while (collator.m_expansion_[offset] != 0) {
\r
1775 m_CEBuffer_[m_CEBufferSize_ ++] =
\r
1776 collator.m_expansion_[++ offset];
\r
1779 // in case of one element expansion, we
\r
1780 // want to immediately return CEpos
\r
1781 if (m_CEBufferSize_ == 1) {
\r
1782 m_CEBufferSize_ = 0;
\r
1783 m_CEBufferOffset_ = 0;
\r
1785 return m_CEBuffer_[0];
\r
1789 * Gets the next digit ce
\r
1790 * @param collator current collator
\r
1791 * @param ce current collation element
\r
1792 * @param cp current codepoint
\r
1793 * @return next digit ce
\r
1795 private int nextDigit(RuleBasedCollator collator, int ce, int cp)
\r
1797 // We do a check to see if we want to collate digits as numbers;
\r
1798 // if so we generate a custom collation key. Otherwise we pull out
\r
1799 // the value stored in the expansion table.
\r
1801 if (m_collator_.m_isNumericCollation_){
\r
1802 int collateVal = 0;
\r
1803 int trailingZeroIndex = 0;
\r
1804 boolean nonZeroValReached = false;
\r
1806 // I just need a temporary place to store my generated CEs.
\r
1807 // icu4c uses a unsigned byte array, i'll use a stringbuffer here
\r
1808 // to avoid dealing with the sign problems and array allocation
\r
1809 // clear and set initial string buffer length
\r
1810 m_utilStringBuffer_.setLength(3);
\r
1812 // We parse the source string until we hit a char that's NOT a
\r
1814 // Use this u_charDigitValue. This might be slow because we have
\r
1815 // to handle surrogates...
\r
1816 int digVal = UCharacter.digit(cp);
\r
1817 // if we have arrived here, we have already processed possible
\r
1818 // supplementaries that trigered the digit tag -
\r
1819 // all supplementaries are marked in the UCA.
\r
1820 // We pad a zero in front of the first element anyways.
\r
1821 // This takes care of the (probably) most common case where
\r
1822 // people are sorting things followed by a single digit
\r
1825 // Make sure we have enough space.
\r
1826 if (digIndx >= ((m_utilStringBuffer_.length() - 2) << 1)) {
\r
1827 m_utilStringBuffer_.setLength(m_utilStringBuffer_.length()
\r
1830 // Skipping over leading zeroes.
\r
1831 if (digVal != 0 || nonZeroValReached) {
\r
1832 if (digVal != 0 && !nonZeroValReached) {
\r
1833 nonZeroValReached = true;
\r
1835 // We parse the digit string into base 100 numbers
\r
1836 // (this fits into a byte).
\r
1837 // We only add to the buffer in twos, thus if we are
\r
1838 // parsing an odd character, that serves as the
\r
1839 // 'tens' digit while the if we are parsing an even
\r
1840 // one, that is the 'ones' digit. We dumped the
\r
1841 // parsed base 100 value (collateVal) into a buffer.
\r
1842 // We multiply each collateVal by 2 (to give us room)
\r
1843 // and add 5 (to avoid overlapping magic CE byte
\r
1844 // values). The last byte we subtract 1 to ensure it is
\r
1845 // less than all the other bytes.
\r
1846 if (digIndx % 2 == 1) {
\r
1847 collateVal += digVal;
\r
1848 // This removes trailing zeroes.
\r
1849 if (collateVal == 0 && trailingZeroIndex == 0) {
\r
1850 trailingZeroIndex = ((digIndx - 1) >>> 1) + 2;
\r
1852 else if (trailingZeroIndex != 0) {
\r
1853 trailingZeroIndex = 0;
\r
1855 m_utilStringBuffer_.setCharAt(
\r
1856 ((digIndx - 1) >>> 1) + 2,
\r
1857 (char)((collateVal << 1) + 6));
\r
1861 // We drop the collation value into the buffer so if
\r
1862 // we need to do a "front patch" we don't have to
\r
1863 // check to see if we're hitting the last element.
\r
1864 collateVal = digVal * 10;
\r
1865 m_utilStringBuffer_.setCharAt((digIndx >>> 1) + 2,
\r
1866 (char)((collateVal << 1) + 6));
\r
1871 // Get next character.
\r
1873 backupInternalState(m_utilSpecialBackUp_);
\r
1874 int char32 = nextChar();
\r
1875 char ch = (char)char32;
\r
1876 if (UTF16.isLeadSurrogate(ch)){
\r
1878 char trail = (char)nextChar();
\r
1879 if (UTF16.isTrailSurrogate(trail)) {
\r
1880 char32 = UCharacterProperty.getRawSupplementary(
\r
1889 digVal = UCharacter.digit(char32);
\r
1890 if (digVal == -1) {
\r
1891 // Resetting position to point to the next unprocessed
\r
1892 // char. We overshot it when doing our test/set for
\r
1894 updateInternalState(m_utilSpecialBackUp_);
\r
1903 if (nonZeroValReached == false){
\r
1905 m_utilStringBuffer_.setCharAt(2, (char)6);
\r
1908 int endIndex = trailingZeroIndex != 0 ? trailingZeroIndex
\r
1909 : (digIndx >>> 1) + 2;
\r
1910 if (digIndx % 2 != 0){
\r
1911 // We missed a value. Since digIndx isn't even, stuck too many
\r
1912 // values into the buffer (this is what we get for padding the
\r
1913 // first byte with a zero). "Front-patch" now by pushing all
\r
1914 // nybbles forward.
\r
1915 // Doing it this way ensures that at least 50% of the time
\r
1916 // (statistically speaking) we'll only be doing a single pass
\r
1917 // and optimizes for strings with single digits. I'm just
\r
1918 // assuming that's the more common case.
\r
1919 for (int i = 2; i < endIndex; i ++){
\r
1920 m_utilStringBuffer_.setCharAt(i,
\r
1921 (char)((((((m_utilStringBuffer_.charAt(i) - 6) >>> 1)
\r
1923 + (((m_utilStringBuffer_.charAt(i + 1) - 6)
\r
1924 >>> 1) / 10) << 1) + 6));
\r
1929 // Subtract one off of the last byte.
\r
1930 m_utilStringBuffer_.setCharAt(endIndex - 1,
\r
1931 (char)(m_utilStringBuffer_.charAt(endIndex - 1) - 1));
\r
1933 // We want to skip over the first two slots in the buffer.
\r
1934 // The first slot is reserved for the header byte CODAN_PLACEHOLDER.
\r
1935 // The second slot is for the sign/exponent byte:
\r
1936 // 0x80 + (decimalPos/2) & 7f.
\r
1937 m_utilStringBuffer_.setCharAt(0, (char)RuleBasedCollator.CODAN_PLACEHOLDER);
\r
1938 m_utilStringBuffer_.setCharAt(1,
\r
1939 (char)(0x80 + ((digIndx >>> 1) & 0x7F)));
\r
1941 // Now transfer the collation key to our collIterate struct.
\r
1942 // The total size for our collation key is endIndx bumped up to the next largest even value divided by two.
\r
1943 ce = (((m_utilStringBuffer_.charAt(0) << 8)
\r
1944 // Primary weight
\r
1945 | m_utilStringBuffer_.charAt(1))
\r
1946 << RuleBasedCollator.CE_PRIMARY_SHIFT_)
\r
1947 // Secondary weight
\r
1948 | (RuleBasedCollator.BYTE_COMMON_
\r
1949 << RuleBasedCollator.CE_SECONDARY_SHIFT_)
\r
1950 | RuleBasedCollator.BYTE_COMMON_; // Tertiary weight.
\r
1951 int i = 2; // Reset the index into the buffer.
\r
1953 m_CEBuffer_[0] = ce;
\r
1954 m_CEBufferSize_ = 1;
\r
1955 m_CEBufferOffset_ = 1;
\r
1956 while (i < endIndex)
\r
1958 int primWeight = m_utilStringBuffer_.charAt(i ++) << 8;
\r
1959 if (i < endIndex) {
\r
1960 primWeight |= m_utilStringBuffer_.charAt(i ++);
\r
1962 m_CEBuffer_[m_CEBufferSize_ ++]
\r
1963 = (primWeight << RuleBasedCollator.CE_PRIMARY_SHIFT_)
\r
1964 | RuleBasedCollator.CE_CONTINUATION_MARKER_;
\r
1969 // no numeric mode, we'll just switch to whatever we stashed and
\r
1971 // find the offset to expansion table
\r
1972 return collator.m_expansion_[getExpansionOffset(collator, ce)];
\r
1976 * Gets the next implicit ce for codepoints
\r
1977 * @param codepoint current codepoint
\r
1978 * @return implicit ce
\r
1980 private int nextImplicit(int codepoint)
\r
1982 if (!UCharacter.isLegal(codepoint)) {
\r
1983 // synwee to check with vladimir on the range of isNonChar()
\r
1984 // illegal code value, use completely ignoreable!
\r
1987 int result = RuleBasedCollator.impCEGen_.getImplicitFromCodePoint(codepoint);
\r
1988 m_CEBuffer_[0] = (result & RuleBasedCollator.CE_PRIMARY_MASK_)
\r
1990 m_CEBuffer_[1] = ((result & 0x0000FFFF) << 16) | 0x000000C0;
\r
1991 m_CEBufferOffset_ = 1;
\r
1992 m_CEBufferSize_ = 2;
\r
1993 return m_CEBuffer_[0];
\r
1997 * Returns the next ce associated with the following surrogate characters
\r
1998 * @param ch current character
\r
2001 private int nextSurrogate(char ch)
\r
2003 int ch_int = nextChar();
\r
2004 char nextch = (char)ch_int;
\r
2005 if (ch_int != CharacterIterator.DONE &&
\r
2006 UTF16.isTrailSurrogate(nextch)) {
\r
2007 int codepoint = UCharacterProperty.getRawSupplementary(ch, nextch);
\r
2008 return nextImplicit(codepoint);
\r
2010 if (nextch != CharacterIterator.DONE) {
\r
2011 previousChar(); // reverts back to the original position
\r
2013 return IGNORABLE; // completely ignorable
\r
2017 * Returns the next ce for a hangul character, this is an implicit
\r
2019 * @param collator current collator
\r
2020 * @param ch current character
\r
2021 * @return hangul ce
\r
2023 private int nextHangul(RuleBasedCollator collator, char ch)
\r
2025 char L = (char)(ch - HANGUL_SBASE_);
\r
2027 // divide into pieces
\r
2028 // do it in this order since some compilers can do % and / in one
\r
2030 char T = (char)(L % HANGUL_TCOUNT_);
\r
2031 L /= HANGUL_TCOUNT_;
\r
2032 char V = (char)(L % HANGUL_VCOUNT_);
\r
2033 L /= HANGUL_VCOUNT_;
\r
2036 L += HANGUL_LBASE_;
\r
2037 V += HANGUL_VBASE_;
\r
2038 T += HANGUL_TBASE_;
\r
2040 // return the first CE, but first put the rest into the expansion
\r
2042 m_CEBufferSize_ = 0;
\r
2043 if (!collator.m_isJamoSpecial_) { // FAST PATH
\r
2044 m_CEBuffer_[m_CEBufferSize_ ++] =
\r
2045 collator.m_trie_.getLeadValue(L);
\r
2046 m_CEBuffer_[m_CEBufferSize_ ++] =
\r
2047 collator.m_trie_.getLeadValue(V);
\r
2049 if (T != HANGUL_TBASE_) {
\r
2050 m_CEBuffer_[m_CEBufferSize_ ++] =
\r
2051 collator.m_trie_.getLeadValue(T);
\r
2053 m_CEBufferOffset_ = 1;
\r
2054 return m_CEBuffer_[0];
\r
2057 // Jamo is Special
\r
2058 // Since Hanguls pass the FCD check, it is guaranteed that we
\r
2059 // won't be in the normalization buffer if something like this
\r
2061 // Move Jamos into normalization buffer
\r
2062 m_buffer_.append(L);
\r
2063 m_buffer_.append(V);
\r
2064 if (T != HANGUL_TBASE_) {
\r
2065 m_buffer_.append(T);
\r
2067 m_FCDLimit_ = m_source_.getIndex();
\r
2068 m_FCDStart_ = m_FCDLimit_ - 1;
\r
2069 // Indicate where to continue in main input string after
\r
2070 // exhausting the buffer
\r
2076 * <p>Special CE management. Expansions, contractions etc...</p>
\r
2077 * @param collator can be plain UCA
\r
2078 * @param ce current ce
\r
2079 * @param ch current character
\r
2080 * @return next special ce
\r
2082 private int nextSpecial(RuleBasedCollator collator, int ce, char ch)
\r
2084 int codepoint = ch;
\r
2085 Backup entrybackup = m_utilSpecialEntryBackUp_;
\r
2086 // this is to handle recursive looping
\r
2087 if (entrybackup != null) {
\r
2088 m_utilSpecialEntryBackUp_ = null;
\r
2091 entrybackup = new Backup();
\r
2093 backupInternalState(entrybackup);
\r
2094 try { // forces it to assign m_utilSpecialEntryBackup_
\r
2096 // This loop will repeat only in the case of contractions,
\r
2098 switch(RuleBasedCollator.getTag(ce)) {
\r
2099 case CE_NOT_FOUND_TAG_:
\r
2100 // impossible case for icu4j
\r
2102 case RuleBasedCollator.CE_SURROGATE_TAG_:
\r
2106 backupInternalState(m_utilSpecialBackUp_);
\r
2107 char trail = (char)nextChar();
\r
2108 ce = nextSurrogate(collator, ce, trail);
\r
2109 // calculate the supplementary code point value,
\r
2110 // if surrogate was not tailored we go one more round
\r
2112 UCharacterProperty.getRawSupplementary(ch, trail);
\r
2114 case CE_SPEC_PROC_TAG_:
\r
2115 ce = nextSpecialPrefix(collator, ce, entrybackup);
\r
2117 case CE_CONTRACTION_TAG_:
\r
2118 ce = nextContraction(collator, ce);
\r
2120 case CE_LONG_PRIMARY_TAG_:
\r
2121 return nextLongPrimary(ce);
\r
2122 case CE_EXPANSION_TAG_:
\r
2123 return nextExpansion(collator, ce);
\r
2124 case CE_DIGIT_TAG_:
\r
2125 ce = nextDigit(collator, ce, codepoint);
\r
2127 // various implicits optimization
\r
2128 case CE_CJK_IMPLICIT_TAG_:
\r
2129 // 0x3400-0x4DB5, 0x4E00-0x9FA5, 0xF900-0xFA2D
\r
2130 return nextImplicit(codepoint);
\r
2131 case CE_IMPLICIT_TAG_: // everything that is not defined
\r
2132 return nextImplicit(codepoint);
\r
2133 case CE_TRAIL_SURROGATE_TAG_:
\r
2134 return IGNORABLE; // DC00-DFFF broken surrogate
\r
2135 case CE_LEAD_SURROGATE_TAG_: // D800-DBFF
\r
2136 return nextSurrogate(ch);
\r
2137 case CE_HANGUL_SYLLABLE_TAG_: // AC00-D7AF
\r
2138 return nextHangul(collator, ch);
\r
2139 case CE_CHARSET_TAG_:
\r
2140 // not yet implemented probably after 1.8
\r
2141 return CE_NOT_FOUND_;
\r
2144 // synwee todo, throw exception or something here.
\r
2146 if (!RuleBasedCollator.isSpecial(ce)) {
\r
2152 m_utilSpecialEntryBackUp_ = entrybackup;
\r
2158 * Special processing is getting a CE that is preceded by a certain prefix.
\r
2159 * Currently this is only needed for optimizing Japanese length and
\r
2160 * iteration marks. When we encouter a special processing tag, we go
\r
2161 * backwards and try to see if we have a match. Contraction tables are used
\r
2162 * - so the whole process is not unlike contraction. prefix data is stored
\r
2163 * backwards in the table.
\r
2164 * @param collator current collator
\r
2165 * @param ce current ce
\r
2166 * @return previous ce
\r
2168 private int previousSpecialPrefix(RuleBasedCollator collator, int ce)
\r
2170 backupInternalState(m_utilSpecialBackUp_);
\r
2172 // position ourselves at the begining of contraction sequence
\r
2173 int offset = getContractionOffset(collator, ce);
\r
2174 int entryoffset = offset;
\r
2175 if (isBackwardsStart()) {
\r
2176 ce = collator.m_contractionCE_[offset];
\r
2179 char prevch = (char)previousChar();
\r
2180 while (prevch > collator.m_contractionIndex_[offset]) {
\r
2181 // since contraction codepoints are ordered, we skip all that
\r
2185 if (prevch == collator.m_contractionIndex_[offset]) {
\r
2186 ce = collator.m_contractionCE_[offset];
\r
2189 // if there is a completely ignorable code point in the middle
\r
2190 // of a prefix, we need to act as if it's not there assumption:
\r
2191 // 'real' noncharacters (*fffe, *ffff, fdd0-fdef are set to
\r
2193 // lone surrogates cannot be set to zero as it would break
\r
2194 // other processing
\r
2195 int isZeroCE = collator.m_trie_.getLeadValue(prevch);
\r
2196 // it's easy for BMP code points
\r
2197 if (isZeroCE == 0) {
\r
2200 else if (UTF16.isTrailSurrogate(prevch)
\r
2201 || UTF16.isLeadSurrogate(prevch)) {
\r
2202 // for supplementary code points, we have to check the next one
\r
2203 // situations where we are going to ignore
\r
2204 // 1. beginning of the string: schar is a lone surrogate
\r
2205 // 2. schar is a lone surrogate
\r
2206 // 3. schar is a trail surrogate in a valid surrogate
\r
2207 // sequence that is explicitly set to zero.
\r
2208 if (!isBackwardsStart()) {
\r
2209 char lead = (char)previousChar();
\r
2210 if (UTF16.isLeadSurrogate(lead)) {
\r
2211 isZeroCE = collator.m_trie_.getLeadValue(lead);
\r
2212 if (RuleBasedCollator.getTag(isZeroCE)
\r
2213 == RuleBasedCollator.CE_SURROGATE_TAG_) {
\r
2214 int finalCE = collator.m_trie_.getTrailValue(
\r
2217 if (finalCE == 0) {
\r
2218 // this is a real, assigned completely
\r
2219 // ignorable code point
\r
2225 nextChar(); // revert to original offset
\r
2226 // lone surrogate, completely ignorable
\r
2229 nextChar(); // revert to original offset
\r
2232 // lone surrogate at the beggining, completely ignorable
\r
2237 // char was not in the table. prefix not found
\r
2238 ce = collator.m_contractionCE_[entryoffset];
\r
2241 if (!isSpecialPrefixTag(ce)) {
\r
2242 // char was in the contraction table, and the corresponding ce
\r
2243 // is not a prefix ce. We found the prefix, break out of loop,
\r
2244 // this ce will end up being returned.
\r
2248 updateInternalState(m_utilSpecialBackUp_);
\r
2253 * Retrieves the previous contraction ce. To ensure that the backwards and
\r
2254 * forwards iteration matches, we take the current region of most possible
\r
2255 * match and pass it through the forward iteration. This will ensure that
\r
2256 * the obstinate problem of overlapping contractions will not occur.
\r
2257 * @param collator current collator
\r
2258 * @param ce current ce
\r
2259 * @param ch current character
\r
2260 * @return previous contraction ce
\r
2262 private int previousContraction(RuleBasedCollator collator, int ce, char ch)
\r
2264 m_utilStringBuffer_.setLength(0);
\r
2265 // since we might encounter normalized characters (from the thai
\r
2266 // processing) we can't use peekCharacter() here.
\r
2267 char prevch = (char)previousChar();
\r
2268 boolean atStart = false;
\r
2269 // TODO: address the comment above - maybe now we *can* use peekCharacter
\r
2270 //while (collator.isUnsafe(ch) || isThaiPreVowel(prevch)) {
\r
2271 while (collator.isUnsafe(ch)) {
\r
2272 m_utilStringBuffer_.insert(0, ch);
\r
2274 if (isBackwardsStart()) {
\r
2278 prevch = (char)previousChar();
\r
2281 // undo the previousChar() if we didn't reach the beginning
\r
2284 // adds the initial base character to the string
\r
2285 m_utilStringBuffer_.insert(0, ch);
\r
2287 // a new collation element iterator is used to simply things, since
\r
2288 // using the current collation element iterator will mean that the
\r
2289 // forward and backwards iteration will share and change the same
\r
2290 // buffers. it is going to be painful.
\r
2291 int originaldecomp = collator.getDecomposition();
\r
2292 // for faster access, since string would have been normalized above
\r
2293 collator.setDecomposition(Collator.NO_DECOMPOSITION);
\r
2294 if (m_utilColEIter_ == null) {
\r
2295 m_utilColEIter_ = new CollationElementIterator(
\r
2296 m_utilStringBuffer_.toString(),
\r
2300 m_utilColEIter_.m_collator_ = collator;
\r
2301 m_utilColEIter_.setText(m_utilStringBuffer_.toString());
\r
2303 ce = m_utilColEIter_.next();
\r
2304 m_CEBufferSize_ = 0;
\r
2305 while (ce != NULLORDER) {
\r
2306 if (m_CEBufferSize_ == m_CEBuffer_.length) {
\r
2308 // increasing cebuffer size
\r
2309 int tempbuffer[] = new int[m_CEBuffer_.length + 50];
\r
2310 System.arraycopy(m_CEBuffer_, 0, tempbuffer, 0,
\r
2311 m_CEBuffer_.length);
\r
2312 m_CEBuffer_ = tempbuffer;
\r
2314 catch( MissingResourceException e)
\r
2318 catch (Exception e) {
\r
2320 e.printStackTrace();
\r
2325 m_CEBuffer_[m_CEBufferSize_ ++] = ce;
\r
2326 ce = m_utilColEIter_.next();
\r
2328 collator.setDecomposition(originaldecomp);
\r
2329 m_CEBufferOffset_ = m_CEBufferSize_ - 1;
\r
2330 return m_CEBuffer_[m_CEBufferOffset_];
\r
2334 * Returns the previous long primary ces
\r
2335 * @param ce long primary ce
\r
2336 * @return previous long primary ces
\r
2338 private int previousLongPrimary(int ce)
\r
2340 m_CEBufferSize_ = 0;
\r
2341 m_CEBuffer_[m_CEBufferSize_ ++] =
\r
2342 ((ce & 0xFFFF00) << 8) | (CE_BYTE_COMMON_ << 8) | CE_BYTE_COMMON_;
\r
2343 m_CEBuffer_[m_CEBufferSize_ ++] = ((ce & 0xFF) << 24)
\r
2344 | RuleBasedCollator.CE_CONTINUATION_MARKER_;
\r
2345 m_CEBufferOffset_ = m_CEBufferSize_ - 1;
\r
2346 return m_CEBuffer_[m_CEBufferOffset_];
\r
2350 * Returns the previous expansion ces
\r
2351 * @param collator current collator
\r
2352 * @param ce current ce
\r
2353 * @return previous expansion ce
\r
2355 private int previousExpansion(RuleBasedCollator collator, int ce)
\r
2357 // find the offset to expansion table
\r
2358 int offset = getExpansionOffset(collator, ce);
\r
2359 m_CEBufferSize_ = getExpansionCount(ce);
\r
2360 if (m_CEBufferSize_ != 0) {
\r
2361 // less than 16 elements in expansion
\r
2362 for (int i = 0; i < m_CEBufferSize_; i ++) {
\r
2363 m_CEBuffer_[i] = collator.m_expansion_[offset + i];
\r
2368 // null terminated ces
\r
2369 while (collator.m_expansion_[offset + m_CEBufferSize_] != 0) {
\r
2370 m_CEBuffer_[m_CEBufferSize_] =
\r
2371 collator.m_expansion_[offset + m_CEBufferSize_];
\r
2372 m_CEBufferSize_ ++;
\r
2375 m_CEBufferOffset_ = m_CEBufferSize_ - 1;
\r
2376 return m_CEBuffer_[m_CEBufferOffset_];
\r
2380 * Getting the digit collation elements
\r
2382 * @param ce current collation element
\r
2383 * @param ch current code point
\r
2384 * @return digit collation element
\r
2386 private int previousDigit(RuleBasedCollator collator, int ce, char ch)
\r
2388 // We do a check to see if we want to collate digits as numbers; if so we generate
\r
2389 // a custom collation key. Otherwise we pull out the value stored in the expansion table.
\r
2390 if (m_collator_.m_isNumericCollation_){
\r
2391 int leadingZeroIndex = 0;
\r
2392 int collateVal = 0;
\r
2393 boolean nonZeroValReached = false;
\r
2395 // clear and set initial string buffer length
\r
2396 m_utilStringBuffer_.setLength(3);
\r
2398 // We parse the source string until we hit a char that's NOT a digit
\r
2399 // Use this u_charDigitValue. This might be slow because we have to
\r
2400 // handle surrogates...
\r
2402 if (UTF16.isTrailSurrogate(ch)) {
\r
2403 if (!isBackwardsStart()){
\r
2404 char lead = (char)previousChar();
\r
2405 if (UTF16.isLeadSurrogate(lead)) {
\r
2406 char32 = UCharacterProperty.getRawSupplementary(lead,
\r
2414 int digVal = UCharacter.digit(char32);
\r
2417 // Make sure we have enough space.
\r
2418 if (digIndx >= ((m_utilStringBuffer_.length() - 2) << 1)) {
\r
2419 m_utilStringBuffer_.setLength(m_utilStringBuffer_.length()
\r
2422 // Skipping over "trailing" zeroes but we still add to digIndx.
\r
2423 if (digVal != 0 || nonZeroValReached) {
\r
2424 if (digVal != 0 && !nonZeroValReached) {
\r
2425 nonZeroValReached = true;
\r
2428 // We parse the digit string into base 100 numbers (this
\r
2429 // fits into a byte).
\r
2430 // We only add to the buffer in twos, thus if we are
\r
2431 // parsing an odd character, that serves as the 'tens'
\r
2432 // digit while the if we are parsing an even one, that is
\r
2433 // the 'ones' digit. We dumped the parsed base 100 value
\r
2434 // (collateVal) into a buffer. We multiply each collateVal
\r
2435 // by 2 (to give us room) and add 5 (to avoid overlapping
\r
2436 // magic CE byte values). The last byte we subtract 1 to
\r
2437 // ensure it is less than all the other bytes.
\r
2438 // Since we're doing in this reverse we want to put the
\r
2439 // first digit encountered into the ones place and the
\r
2440 // second digit encountered into the tens place.
\r
2442 if (digIndx % 2 == 1){
\r
2443 collateVal += digVal * 10;
\r
2445 // This removes leading zeroes.
\r
2446 if (collateVal == 0 && leadingZeroIndex == 0) {
\r
2447 leadingZeroIndex = ((digIndx - 1) >>> 1) + 2;
\r
2449 else if (leadingZeroIndex != 0) {
\r
2450 leadingZeroIndex = 0;
\r
2453 m_utilStringBuffer_.setCharAt(((digIndx - 1) >>> 1) + 2,
\r
2454 (char)((collateVal << 1) + 6));
\r
2458 collateVal = digVal;
\r
2463 if (!isBackwardsStart()){
\r
2464 backupInternalState(m_utilSpecialBackUp_);
\r
2465 char32 = previousChar();
\r
2466 if (UTF16.isTrailSurrogate(ch)){
\r
2467 if (!isBackwardsStart()) {
\r
2468 char lead = (char)previousChar();
\r
2469 if (UTF16.isLeadSurrogate(lead)) {
\r
2471 = UCharacterProperty.getRawSupplementary(
\r
2475 updateInternalState(m_utilSpecialBackUp_);
\r
2480 digVal = UCharacter.digit(char32);
\r
2481 if (digVal == -1) {
\r
2482 updateInternalState(m_utilSpecialBackUp_);
\r
2491 if (nonZeroValReached == false) {
\r
2493 m_utilStringBuffer_.setCharAt(2, (char)6);
\r
2496 if (digIndx % 2 != 0) {
\r
2497 if (collateVal == 0 && leadingZeroIndex == 0) {
\r
2498 // This removes the leading 0 in a odd number sequence of
\r
2499 // numbers e.g. avery001
\r
2500 leadingZeroIndex = ((digIndx - 1) >>> 1) + 2;
\r
2503 // this is not a leading 0, we add it in
\r
2504 m_utilStringBuffer_.setCharAt((digIndx >>> 1) + 2,
\r
2505 (char)((collateVal << 1) + 6));
\r
2510 int endIndex = leadingZeroIndex != 0 ? leadingZeroIndex
\r
2511 : ((digIndx >>> 1) + 2) ;
\r
2512 digIndx = ((endIndex - 2) << 1) + 1; // removing initial zeros
\r
2513 // Subtract one off of the last byte.
\r
2514 // Really the first byte here, but it's reversed...
\r
2515 m_utilStringBuffer_.setCharAt(2,
\r
2516 (char)(m_utilStringBuffer_.charAt(2) - 1));
\r
2517 // We want to skip over the first two slots in the buffer.
\r
2518 // The first slot is reserved for the header byte CODAN_PLACEHOLDER.
\r
2519 // The second slot is for the sign/exponent byte:
\r
2520 // 0x80 + (decimalPos/2) & 7f.
\r
2521 m_utilStringBuffer_.setCharAt(0, (char)RuleBasedCollator.CODAN_PLACEHOLDER);
\r
2522 m_utilStringBuffer_.setCharAt(1,
\r
2523 (char)(0x80 + ((digIndx >>> 1) & 0x7F)));
\r
2525 // Now transfer the collation key to our collIterate struct.
\r
2526 // The total size for our collation key is endIndx bumped up to the
\r
2527 // next largest even value divided by two.
\r
2528 m_CEBufferSize_ = 0;
\r
2529 m_CEBuffer_[m_CEBufferSize_ ++]
\r
2530 = (((m_utilStringBuffer_.charAt(0) << 8)
\r
2531 // Primary weight
\r
2532 | m_utilStringBuffer_.charAt(1))
\r
2533 << RuleBasedCollator.CE_PRIMARY_SHIFT_)
\r
2534 // Secondary weight
\r
2535 | (RuleBasedCollator.BYTE_COMMON_
\r
2536 << RuleBasedCollator.CE_SECONDARY_SHIFT_)
\r
2537 // Tertiary weight.
\r
2538 | RuleBasedCollator.BYTE_COMMON_;
\r
2539 int i = endIndex - 1; // Reset the index into the buffer.
\r
2541 int primWeight = m_utilStringBuffer_.charAt(i --) << 8;
\r
2543 primWeight |= m_utilStringBuffer_.charAt(i --);
\r
2545 m_CEBuffer_[m_CEBufferSize_ ++]
\r
2546 = (primWeight << RuleBasedCollator.CE_PRIMARY_SHIFT_)
\r
2547 | RuleBasedCollator.CE_CONTINUATION_MARKER_;
\r
2549 m_CEBufferOffset_ = m_CEBufferSize_ - 1;
\r
2550 return m_CEBuffer_[m_CEBufferOffset_];
\r
2553 return collator.m_expansion_[getExpansionOffset(collator, ce)];
\r
2558 * Returns previous hangul ces
\r
2559 * @param collator current collator
\r
2560 * @param ch current character
\r
2561 * @return previous hangul ce
\r
2563 private int previousHangul(RuleBasedCollator collator, char ch)
\r
2565 char L = (char)(ch - HANGUL_SBASE_);
\r
2566 // we do it in this order since some compilers can do % and / in one
\r
2568 char T = (char)(L % HANGUL_TCOUNT_);
\r
2569 L /= HANGUL_TCOUNT_;
\r
2570 char V = (char)(L % HANGUL_VCOUNT_);
\r
2571 L /= HANGUL_VCOUNT_;
\r
2574 L += HANGUL_LBASE_;
\r
2575 V += HANGUL_VBASE_;
\r
2576 T += HANGUL_TBASE_;
\r
2578 m_CEBufferSize_ = 0;
\r
2579 if (!collator.m_isJamoSpecial_) {
\r
2580 m_CEBuffer_[m_CEBufferSize_ ++] =
\r
2581 collator.m_trie_.getLeadValue(L);
\r
2582 m_CEBuffer_[m_CEBufferSize_ ++] =
\r
2583 collator.m_trie_.getLeadValue(V);
\r
2584 if (T != HANGUL_TBASE_) {
\r
2585 m_CEBuffer_[m_CEBufferSize_ ++] =
\r
2586 collator.m_trie_.getLeadValue(T);
\r
2588 m_CEBufferOffset_ = m_CEBufferSize_ - 1;
\r
2589 return m_CEBuffer_[m_CEBufferOffset_];
\r
2592 // Since Hanguls pass the FCD check, it is guaranteed that we won't
\r
2593 // be in the normalization buffer if something like this happens
\r
2594 // Move Jamos into normalization buffer
\r
2595 m_buffer_.append(L);
\r
2596 m_buffer_.append(V);
\r
2597 if (T != HANGUL_TBASE_) {
\r
2598 m_buffer_.append(T);
\r
2601 m_FCDStart_ = m_source_.getIndex();
\r
2602 m_FCDLimit_ = m_FCDStart_ + 1;
\r
2608 * Gets implicit codepoint ces
\r
2609 * @param codepoint current codepoint
\r
2610 * @return implicit codepoint ces
\r
2612 private int previousImplicit(int codepoint)
\r
2614 if (!UCharacter.isLegal(codepoint)) {
\r
2615 return IGNORABLE; // illegal code value, completely ignoreable!
\r
2617 int result = RuleBasedCollator.impCEGen_.getImplicitFromCodePoint(codepoint);
\r
2618 m_CEBufferSize_ = 2;
\r
2619 m_CEBufferOffset_ = 1;
\r
2620 m_CEBuffer_[0] = (result & RuleBasedCollator.CE_PRIMARY_MASK_)
\r
2622 m_CEBuffer_[1] = ((result & 0x0000FFFF) << 16) | 0x000000C0;
\r
2623 return m_CEBuffer_[1];
\r
2627 * Gets the previous surrogate ce
\r
2628 * @param ch current character
\r
2629 * @return previous surrogate ce
\r
2631 private int previousSurrogate(char ch)
\r
2633 if (isBackwardsStart()) {
\r
2634 // we are at the start of the string, wrong place to be at
\r
2637 char prevch = (char)previousChar();
\r
2638 // Handles Han and Supplementary characters here.
\r
2639 if (UTF16.isLeadSurrogate(prevch)) {
\r
2640 return previousImplicit(
\r
2641 UCharacterProperty.getRawSupplementary(prevch, ch));
\r
2643 if (prevch != CharacterIterator.DONE) {
\r
2646 return IGNORABLE; // completely ignorable
\r
2650 * <p>Special CE management. Expansions, contractions etc...</p>
\r
2651 * @param collator can be plain UCA
\r
2652 * @param ce current ce
\r
2653 * @param ch current character
\r
2654 * @return previous special ce
\r
2656 private int previousSpecial(RuleBasedCollator collator, int ce, char ch)
\r
2659 // the only ces that loops are thai, special prefix and
\r
2661 switch (RuleBasedCollator.getTag(ce)) {
\r
2662 case CE_NOT_FOUND_TAG_: // this tag always returns
\r
2664 case RuleBasedCollator.CE_SURROGATE_TAG_:
\r
2665 // essentialy a disengaged lead surrogate. a broken
\r
2666 // sequence was encountered and this is an error
\r
2668 case CE_SPEC_PROC_TAG_:
\r
2669 ce = previousSpecialPrefix(collator, ce);
\r
2671 case CE_CONTRACTION_TAG_:
\r
2672 // may loop for first character e.g. "0x0f71" for english
\r
2673 if (isBackwardsStart()) {
\r
2674 // start of string or this is not the end of any contraction
\r
2675 ce = collator.m_contractionCE_[
\r
2676 getContractionOffset(collator, ce)];
\r
2679 return previousContraction(collator, ce, ch); // else
\r
2680 case CE_LONG_PRIMARY_TAG_:
\r
2681 return previousLongPrimary(ce);
\r
2682 case CE_EXPANSION_TAG_: // always returns
\r
2683 return previousExpansion(collator, ce);
\r
2684 case CE_DIGIT_TAG_:
\r
2685 ce = previousDigit(collator, ce, ch);
\r
2687 case CE_HANGUL_SYLLABLE_TAG_: // AC00-D7AF
\r
2688 return previousHangul(collator, ch);
\r
2689 case CE_LEAD_SURROGATE_TAG_: // D800-DBFF
\r
2690 return IGNORABLE; // broken surrogate sequence
\r
2691 case CE_TRAIL_SURROGATE_TAG_: // DC00-DFFF
\r
2692 return previousSurrogate(ch);
\r
2693 case CE_CJK_IMPLICIT_TAG_:
\r
2694 // 0x3400-0x4DB5, 0x4E00-0x9FA5, 0xF900-0xFA2D
\r
2695 return previousImplicit(ch);
\r
2696 case CE_IMPLICIT_TAG_: // everything that is not defined
\r
2697 // UCA is filled with these. Tailorings are NOT_FOUND
\r
2698 return previousImplicit(ch);
\r
2699 case CE_CHARSET_TAG_: // this tag always returns
\r
2700 return CE_NOT_FOUND_;
\r
2701 default: // this tag always returns
\r
2704 if (!RuleBasedCollator.isSpecial(ce)) {
\r
2712 * GET IMPLICIT PRIMARY WEIGHTS
\r
2713 * @param cp codepoint
\r
2714 * @param value is left justified primary key
\r
2716 // private static final int getImplicitPrimary(int cp)
\r
2718 // cp = swapCJK(cp);
\r
2720 // //if (DEBUG) System.out.println("CJK swapped: " + Utility.hex(cp));
\r
2721 // // we now have a range of numbers from 0 to 21FFFF.
\r
2722 // // we must skip all 00, 01, 02 bytes, so most bytes have 253 values
\r
2723 // // we must leave a gap of 01 between all values of the last byte, so
\r
2724 // // the last byte has 126 values (3 byte case)
\r
2725 // // we shift so that HAN all has the same first primary, for
\r
2726 // // compression.
\r
2727 // // for the 4 byte case, we make the gap as large as we can fit.
\r
2728 // // Three byte forms are EC xx xx, ED xx xx, EE xx xx (with a gap of 1)
\r
2729 // // Four byte forms (most supplementaries) are EF xx xx xx (with a gap
\r
2730 // // of LAST2_MULTIPLIER == 14)
\r
2732 // int last0 = cp - RuleBasedCollator.IMPLICIT_4BYTE_BOUNDARY_;
\r
2733 // if (last0 < 0) {
\r
2734 // int last1 = cp / RuleBasedCollator.LAST_COUNT_;
\r
2735 // last0 = cp % RuleBasedCollator.LAST_COUNT_;
\r
2737 // int last2 = last1 / RuleBasedCollator.OTHER_COUNT_;
\r
2738 // last1 %= RuleBasedCollator.OTHER_COUNT_;
\r
2739 // return RuleBasedCollator.IMPLICIT_BASE_3BYTE_ + (last2 << 24)
\r
2740 // + (last1 << 16)
\r
2741 // + ((last0 * RuleBasedCollator.LAST_MULTIPLIER_) << 8);
\r
2744 // int last1 = last0 / RuleBasedCollator.LAST_COUNT2_;
\r
2745 // last0 %= RuleBasedCollator.LAST_COUNT2_;
\r
2747 // int last2 = last1 / RuleBasedCollator.OTHER_COUNT_;
\r
2748 // last1 %= RuleBasedCollator.OTHER_COUNT_;
\r
2750 // int last3 = last2 / RuleBasedCollator.OTHER_COUNT_;
\r
2751 // last2 %= RuleBasedCollator.OTHER_COUNT_;
\r
2752 // return RuleBasedCollator.IMPLICIT_BASE_4BYTE_ + (last3 << 24)
\r
2753 // + (last2 << 16) + (last1 << 8)
\r
2754 // + (last0 * RuleBasedCollator.LAST2_MULTIPLIER_);
\r
2759 // * Swapping CJK characters for implicit ces
\r
2760 // * @param cp codepoint CJK
\r
2761 // * @return swapped result
\r
2763 // private static final int swapCJK(int cp)
\r
2765 // if (cp >= CJK_BASE_) {
\r
2766 // if (cp < CJK_LIMIT_) {
\r
2767 // return cp - CJK_BASE_;
\r
2769 // if (cp < CJK_COMPAT_USED_BASE_) {
\r
2770 // return cp + NON_CJK_OFFSET_;
\r
2772 // if (cp < CJK_COMPAT_USED_LIMIT_) {
\r
2773 // return cp - CJK_COMPAT_USED_BASE_ + (CJK_LIMIT_ - CJK_BASE_);
\r
2775 // if (cp < CJK_B_BASE_) {
\r
2776 // return cp + NON_CJK_OFFSET_;
\r
2778 // if (cp < CJK_B_LIMIT_) {
\r
2779 // return cp; // non-BMP-CJK
\r
2781 // return cp + NON_CJK_OFFSET_; // non-CJK
\r
2783 // if (cp < CJK_A_BASE_) {
\r
2784 // return cp + NON_CJK_OFFSET_;
\r
2786 // if (cp < CJK_A_LIMIT_) {
\r
2787 // return cp - CJK_A_BASE_ + (CJK_LIMIT_ - CJK_BASE_)
\r
2788 // + (CJK_COMPAT_USED_LIMIT_ - CJK_COMPAT_USED_BASE_);
\r
2790 // return cp + NON_CJK_OFFSET_; // non-CJK
\r
2794 // * Gets a character from the source string at a given offset.
\r
2795 // * Handles both normal and iterative cases.
\r
2796 // * No error checking and does not access the normalization buffer
\r
2797 // * - caller beware!
\r
2798 // * @param offset offset from current position which character is to be
\r
2800 // * @return character at current position + offset
\r
2802 // private char peekCharacter(int offset)
\r
2804 // if (offset != 0) {
\r
2805 // int currentoffset = m_source_.getIndex();
\r
2806 // m_source_.setIndex(currentoffset + offset);
\r
2807 // char result = (char)m_source_.current();
\r
2808 // m_source_.setIndex(currentoffset);
\r
2812 // return (char)m_source_.current();
\r
2817 * Moves back 1 position in the source string. This is slightly less
\r
2818 * complicated than previousChar in that it doesn't normalize while
\r
2819 * moving back. Boundary checks are not performed.
\r
2820 * This method is to be used with caution, with the assumption that
\r
2821 * moving back one position will not exceed the source limits.
\r
2822 * Use only with nextChar() and never call this API twice in a row without
\r
2823 * nextChar() in the middle.
\r
2825 private void goBackOne()
\r
2827 if (m_bufferOffset_ >= 0) {
\r
2828 m_bufferOffset_ --;
\r
2831 m_source_.setIndex(m_source_.getIndex() - 1);
\r
2836 * Moves forward 1 position in the source string. This is slightly less
\r
2837 * complicated than nextChar in that it doesn't normalize while
\r
2838 * moving back. Boundary checks are not performed.
\r
2839 * This method is to be used with caution, with the assumption that
\r
2840 * moving back one position will not exceed the source limits.
\r
2841 * Use only with previousChar() and never call this API twice in a row
\r
2842 * without previousChar() in the middle.
\r
2844 private void goForwardOne()
\r
2846 if (m_bufferOffset_ < 0) {
\r
2847 // we're working on the source and not normalizing. fast path.
\r
2848 // note Thai pre-vowel reordering uses buffer too
\r
2849 m_source_.setIndex(m_source_.getIndex() + 1);
\r
2852 // we are in the buffer, buffer offset will never be 0 here
\r
2853 m_bufferOffset_ ++;
\r