Showing the changes to java/pkg/java/text/CollationElementIterator.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,777 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | /* | ||
| 27 | * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved | ||
| 28 | * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved | ||
| 29 | * | ||
| 30 | * The original version of this source code and documentation is copyrighted | ||
| 31 | * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These | ||
| 32 | * materials are provided under terms of a License Agreement between Taligent | ||
| 33 | * and Sun. This technology is protected by multiple US and International | ||
| 34 | * patents. This notice and attribution to Taligent may not be removed. | ||
| 35 | * Taligent is a registered trademark of Taligent, Inc. | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | package java.text; | ||
| 40 | |||
| 41 | import java.lang.Character; | ||
| 42 | import java.util.Vector; | ||
| 43 | import sun.text.CollatorUtilities; | ||
| 44 | import sun.text.normalizer.NormalizerBase; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * The <code>CollationElementIterator</code> class is used as an iterator | ||
| 48 | * to walk through each character of an international string. Use the iterator | ||
| 49 | * to return the ordering priority of the positioned character. The ordering | ||
| 50 | * priority of a character, which we refer to as a key, defines how a character | ||
| 51 | * is collated in the given collation object. | ||
| 52 | * | ||
| 53 | * <p> | ||
| 54 | * For example, consider the following in Spanish: | ||
| 55 | * <blockquote> | ||
| 56 | * <pre> | ||
| 57 | * "ca" -> the first key is key('c') and second key is key('a'). | ||
| 58 | * "cha" -> the first key is key('ch') and second key is key('a'). | ||
| 59 | * </pre> | ||
| 60 | * </blockquote> | ||
| 61 | * And in German, | ||
| 62 | * <blockquote> | ||
| 63 | * <pre> | ||
| 64 | * "\u00e4b"-> the first key is key('a'), the second key is key('e'), and | ||
| 65 | * the third key is key('b'). | ||
| 66 | * </pre> | ||
| 67 | * </blockquote> | ||
| 68 | * The key of a character is an integer composed of primary order(short), | ||
| 69 | * secondary order(byte), and tertiary order(byte). Java strictly defines | ||
| 70 | * the size and signedness of its primitive data types. Therefore, the static | ||
| 71 | * functions <code>primaryOrder</code>, <code>secondaryOrder</code>, and | ||
| 72 | * <code>tertiaryOrder</code> return <code>int</code>, <code>short</code>, | ||
| 73 | * and <code>short</code> respectively to ensure the correctness of the key | ||
| 74 | * value. | ||
| 75 | * | ||
| 76 | * <p> | ||
| 77 | * Example of the iterator usage, | ||
| 78 | * <blockquote> | ||
| 79 | * <pre> | ||
| 80 | * | ||
| 81 | * String testString = "This is a test"; | ||
| 82 | * Collator col = Collator.getInstance(); | ||
| 83 | * if (col instanceof RuleBasedCollator) { | ||
| 84 | * RuleBasedCollator ruleBasedCollator = (RuleBasedCollator)col; | ||
| 85 | * CollationElementIterator collationElementIterator = ruleBasedCollator.getCollationElementIterator(testString); | ||
| 86 | * int primaryOrder = CollationElementIterator.primaryOrder(collationElementIterator.next()); | ||
| 87 | * : | ||
| 88 | * } | ||
| 89 | * </pre> | ||
| 90 | * </blockquote> | ||
| 91 | * | ||
| 92 | * <p> | ||
| 93 | * <code>CollationElementIterator.next</code> returns the collation order | ||
| 94 | * of the next character. A collation order consists of primary order, | ||
| 95 | * secondary order and tertiary order. The data type of the collation | ||
| 96 | * order is <strong>int</strong>. The first 16 bits of a collation order | ||
| 97 | * is its primary order; the next 8 bits is the secondary order and the | ||
| 98 | * last 8 bits is the tertiary order. | ||
| 99 | * | ||
| 100 | * <p><b>Note:</b> <code>CollationElementIterator</code> is a part of | ||
| 101 | * <code>RuleBasedCollator</code> implementation. It is only usable | ||
| 102 | * with <code>RuleBasedCollator</code> instances. | ||
| 103 | * | ||
| 104 | * @see Collator | ||
| 105 | * @see RuleBasedCollator | ||
| 106 | * @author Helena Shih, Laura Werner, Richard Gillam | ||
| 107 | */ | ||
| 108 | public final class CollationElementIterator | ||
| 109 | { | ||
| 110 | /** | ||
| 111 | * Null order which indicates the end of string is reached by the | ||
| 112 | * cursor. | ||
| 113 | */ | ||
| 114 | public final static int NULLORDER = 0xffffffff; | ||
| 115 | |||
| 116 | /** | ||
| 117 | * CollationElementIterator constructor. This takes the source string and | ||
| 118 | * the collation object. The cursor will walk thru the source string based | ||
| 119 | * on the predefined collation rules. If the source string is empty, | ||
| 120 | * NULLORDER will be returned on the calls to next(). | ||
| 121 | * @param sourceText the source string. | ||
| 122 | * @param order the collation object. | ||
| 123 | */ | ||
| 124 | CollationElementIterator(String sourceText, RuleBasedCollator owner) { | ||
| 125 | this.owner = owner; | ||
| 126 | ordering = owner.getTables(); | ||
| 127 | if ( sourceText.length() != 0 ) { | ||
| 128 | NormalizerBase.Mode mode = | ||
| 129 | CollatorUtilities.toNormalizerMode(owner.getDecomposition()); | ||
| 130 | text = new NormalizerBase(sourceText, mode); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * CollationElementIterator constructor. This takes the source string and | ||
| 136 | * the collation object. The cursor will walk thru the source string based | ||
| 137 | * on the predefined collation rules. If the source string is empty, | ||
| 138 | * NULLORDER will be returned on the calls to next(). | ||
| 139 | * @param sourceText the source string. | ||
| 140 | * @param order the collation object. | ||
| 141 | */ | ||
| 142 | CollationElementIterator(CharacterIterator sourceText, RuleBasedCollator owner) { | ||
| 143 | this.owner = owner; | ||
| 144 | ordering = owner.getTables(); | ||
| 145 | NormalizerBase.Mode mode = | ||
| 146 | CollatorUtilities.toNormalizerMode(owner.getDecomposition()); | ||
| 147 | text = new NormalizerBase(sourceText, mode); | ||
| 148 | } | ||
| 149 | |||
| 150 | /** | ||
| 151 | * Resets the cursor to the beginning of the string. The next call | ||
| 152 | * to next() will return the first collation element in the string. | ||
| 153 | */ | ||
| 154 | public void reset() | ||
| 155 | { | ||
| 156 | if (text != null) { | ||
| 157 | text.reset(); | ||
| 158 | NormalizerBase.Mode mode = | ||
| 159 | CollatorUtilities.toNormalizerMode(owner.getDecomposition()); | ||
| 160 | text.setMode(mode); | ||
| 161 | } | ||
| 162 | buffer = null; | ||
| 163 | expIndex = 0; | ||
| 164 | swapOrder = 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | /** | ||
| 168 | * Get the next collation element in the string. <p>This iterator iterates | ||
| 169 | * over a sequence of collation elements that were built from the string. | ||
| 170 | * Because there isn't necessarily a one-to-one mapping from characters to | ||
| 171 | * collation elements, this doesn't mean the same thing as "return the | ||
| 172 | * collation element [or ordering priority] of the next character in the | ||
| 173 | * string".</p> | ||
| 174 | * <p>This function returns the collation element that the iterator is currently | ||
| 175 | * pointing to and then updates the internal pointer to point to the next element. | ||
| 176 | * previous() updates the pointer first and then returns the element. This | ||
| 177 | * means that when you change direction while iterating (i.e., call next() and | ||
| 178 | * then call previous(), or call previous() and then call next()), you'll get | ||
| 179 | * back the same element twice.</p> | ||
| 180 | */ | ||
| 181 | public int next() | ||
| 182 | { | ||
| 183 | if (text == null) { | ||
| 184 | return NULLORDER; | ||
| 185 | } | ||
| 186 | NormalizerBase.Mode textMode = text.getMode(); | ||
| 187 | // convert the owner's mode to something the Normalizer understands | ||
| 188 | NormalizerBase.Mode ownerMode = | ||
| 189 | CollatorUtilities.toNormalizerMode(owner.getDecomposition()); | ||
| 190 | if (textMode != ownerMode) { | ||
| 191 | text.setMode(ownerMode); | ||
| 192 | } | ||
| 193 | |||
| 194 | // if buffer contains any decomposed char values | ||
| 195 | // return their strength orders before continuing in | ||
| 196 | // the Normalizer's CharacterIterator. | ||
| 197 | if (buffer != null) { | ||
| 198 | if (expIndex < buffer.length) { | ||
| 199 | return strengthOrder(buffer[expIndex++]); | ||
| 200 | } else { | ||
| 201 | buffer = null; | ||
| 202 | expIndex = 0; | ||
| 203 | } | ||
| 204 | } else if (swapOrder != 0) { | ||
| 205 | if (Character.isSupplementaryCodePoint(swapOrder)) { | ||
| 206 | char[] chars = Character.toChars(swapOrder); | ||
| 207 | swapOrder = chars[1]; | ||
| 208 | return chars[0] << 16; | ||
| 209 | } | ||
| 210 | int order = swapOrder << 16; | ||
| 211 | swapOrder = 0; | ||
| 212 | return order; | ||
| 213 | } | ||
| 214 | int ch = text.next(); | ||
| 215 | |||
| 216 | // are we at the end of Normalizer's text? | ||
| 217 | if (ch == NormalizerBase.DONE) { | ||
| 218 | return NULLORDER; | ||
| 219 | } | ||
| 220 | |||
| 221 | int value = ordering.getUnicodeOrder(ch); | ||
| 222 | if (value == RuleBasedCollator.UNMAPPED) { | ||
| 223 | swapOrder = ch; | ||
| 224 | return UNMAPPEDCHARVALUE; | ||
| 225 | } | ||
| 226 | else if (value >= RuleBasedCollator.CONTRACTCHARINDEX) { | ||
| 227 | value = nextContractChar(ch); | ||
| 228 | } | ||
| 229 | if (value >= RuleBasedCollator.EXPANDCHARINDEX) { | ||
| 230 | buffer = ordering.getExpandValueList(value); | ||
| 231 | expIndex = 0; | ||
| 232 | value = buffer[expIndex++]; | ||
| 233 | } | ||
| 234 | |||
| 235 | if (ordering.isSEAsianSwapping()) { | ||
| 236 | int consonant; | ||
| 237 | if (isThaiPreVowel(ch)) { | ||
| 238 | consonant = text.next(); | ||
| 239 | if (isThaiBaseConsonant(consonant)) { | ||
| 240 | buffer = makeReorderedBuffer(consonant, value, buffer, true); | ||
| 241 | value = buffer[0]; | ||
| 242 | expIndex = 1; | ||
| 243 | } else if (consonant != NormalizerBase.DONE) { | ||
| 244 | text.previous(); | ||
| 245 | } | ||
| 246 | } | ||
| 247 | if (isLaoPreVowel(ch)) { | ||
| 248 | consonant = text.next(); | ||
| 249 | if (isLaoBaseConsonant(consonant)) { | ||
| 250 | buffer = makeReorderedBuffer(consonant, value, buffer, true); | ||
| 251 | value = buffer[0]; | ||
| 252 | expIndex = 1; | ||
| 253 | } else if (consonant != NormalizerBase.DONE) { | ||
| 254 | text.previous(); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | return strengthOrder(value); | ||
| 260 | } | ||
| 261 | |||
| 262 | /** | ||
| 263 | * Get the previous collation element in the string. <p>This iterator iterates | ||
| 264 | * over a sequence of collation elements that were built from the string. | ||
| 265 | * Because there isn't necessarily a one-to-one mapping from characters to | ||
| 266 | * collation elements, this doesn't mean the same thing as "return the | ||
| 267 | * collation element [or ordering priority] of the previous character in the | ||
| 268 | * string".</p> | ||
| 269 | * <p>This function updates the iterator's internal pointer to point to the | ||
| 270 | * collation element preceding the one it's currently pointing to and then | ||
| 271 | * returns that element, while next() returns the current element and then | ||
| 272 | * updates the pointer. This means that when you change direction while | ||
| 273 | * iterating (i.e., call next() and then call previous(), or call previous() | ||
| 274 | * and then call next()), you'll get back the same element twice.</p> | ||
| 275 | * @since 1.2 | ||
| 276 | */ | ||
| 277 | public int previous() | ||
| 278 | { | ||
| 279 | if (text == null) { | ||
| 280 | return NULLORDER; | ||
| 281 | } | ||
| 282 | NormalizerBase.Mode textMode = text.getMode(); | ||
| 283 | // convert the owner's mode to something the Normalizer understands | ||
| 284 | NormalizerBase.Mode ownerMode = | ||
| 285 | CollatorUtilities.toNormalizerMode(owner.getDecomposition()); | ||
| 286 | if (textMode != ownerMode) { | ||
| 287 | text.setMode(ownerMode); | ||
| 288 | } | ||
| 289 | if (buffer != null) { | ||
| 290 | if (expIndex > 0) { | ||
| 291 | return strengthOrder(buffer[--expIndex]); | ||
| 292 | } else { | ||
| 293 | buffer = null; | ||
| 294 | expIndex = 0; | ||
| 295 | } | ||
| 296 | } else if (swapOrder != 0) { | ||
| 297 | if (Character.isSupplementaryCodePoint(swapOrder)) { | ||
| 298 | char[] chars = Character.toChars(swapOrder); | ||
| 299 | swapOrder = chars[1]; | ||
| 300 | return chars[0] << 16; | ||
| 301 | } | ||
| 302 | int order = swapOrder << 16; | ||
| 303 | swapOrder = 0; | ||
| 304 | return order; | ||
| 305 | } | ||
| 306 | int ch = text.previous(); | ||
| 307 | if (ch == NormalizerBase.DONE) { | ||
| 308 | return NULLORDER; | ||
| 309 | } | ||
| 310 | |||
| 311 | int value = ordering.getUnicodeOrder(ch); | ||
| 312 | |||
| 313 | if (value == RuleBasedCollator.UNMAPPED) { | ||
| 314 | swapOrder = UNMAPPEDCHARVALUE; | ||
| 315 | return ch; | ||
| 316 | } else if (value >= RuleBasedCollator.CONTRACTCHARINDEX) { | ||
| 317 | value = prevContractChar(ch); | ||
| 318 | } | ||
| 319 | if (value >= RuleBasedCollator.EXPANDCHARINDEX) { | ||
| 320 | buffer = ordering.getExpandValueList(value); | ||
| 321 | expIndex = buffer.length; | ||
| 322 | value = buffer[--expIndex]; | ||
| 323 | } | ||
| 324 | |||
| 325 | if (ordering.isSEAsianSwapping()) { | ||
| 326 | int vowel; | ||
| 327 | if (isThaiBaseConsonant(ch)) { | ||
| 328 | vowel = text.previous(); | ||
| 329 | if (isThaiPreVowel(vowel)) { | ||
| 330 | buffer = makeReorderedBuffer(vowel, value, buffer, false); | ||
| 331 | expIndex = buffer.length - 1; | ||
| 332 | value = buffer[expIndex]; | ||
| 333 | } else { | ||
| 334 | text.next(); | ||
| 335 | } | ||
| 336 | } | ||
| 337 | if (isLaoBaseConsonant(ch)) { | ||
| 338 | vowel = text.previous(); | ||
| 339 | if (isLaoPreVowel(vowel)) { | ||
| 340 | buffer = makeReorderedBuffer(vowel, value, buffer, false); | ||
| 341 | expIndex = buffer.length - 1; | ||
| 342 | value = buffer[expIndex]; | ||
| 343 | } else { | ||
| 344 | text.next(); | ||
| 345 | } | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | return strengthOrder(value); | ||
| 350 | } | ||
| 351 | |||
| 352 | /** | ||
| 353 | * Return the primary component of a collation element. | ||
| 354 | * @param order the collation element | ||
| 355 | * @return the element's primary component | ||
| 356 | */ | ||
| 357 | public final static int primaryOrder(int order) | ||
| 358 | { | ||
| 359 | order &= RBCollationTables.PRIMARYORDERMASK; | ||
| 360 | return (order >>> RBCollationTables.PRIMARYORDERSHIFT); | ||
| 361 | } | ||
| 362 | /** | ||
| 363 | * Return the secondary component of a collation element. | ||
| 364 | * @param order the collation element | ||
| 365 | * @return the element's secondary component | ||
| 366 | */ | ||
| 367 | public final static short secondaryOrder(int order) | ||
| 368 | { | ||
| 369 | order = order & RBCollationTables.SECONDARYORDERMASK; | ||
| 370 | return ((short)(order >> RBCollationTables.SECONDARYORDERSHIFT)); | ||
| 371 | } | ||
| 372 | /** | ||
| 373 | * Return the tertiary component of a collation element. | ||
| 374 | * @param order the collation element | ||
| 375 | * @return the element's tertiary component | ||
| 376 | */ | ||
| 377 | public final static short tertiaryOrder(int order) | ||
| 378 | { | ||
| 379 | return ((short)(order &= RBCollationTables.TERTIARYORDERMASK)); | ||
| 380 | } | ||
| 381 | |||
| 382 | /** | ||
| 383 | * Get the comparison order in the desired strength. Ignore the other | ||
| 384 | * differences. | ||
| 385 | * @param order The order value | ||
| 386 | */ | ||
| 387 | final int strengthOrder(int order) | ||
| 388 | { | ||
| 389 | int s = owner.getStrength(); | ||
| 390 | if (s == Collator.PRIMARY) | ||
| 391 | { | ||
| 392 | order &= RBCollationTables.PRIMARYDIFFERENCEONLY; | ||
| 393 | } else if (s == Collator.SECONDARY) | ||
| 394 | { | ||
| 395 | order &= RBCollationTables.SECONDARYDIFFERENCEONLY; | ||
| 396 | } | ||
| 397 | return order; | ||
| 398 | } | ||
| 399 | |||
| 400 | /** | ||
| 401 | * Sets the iterator to point to the collation element corresponding to | ||
| 402 | * the specified character (the parameter is a CHARACTER offset in the | ||
| 403 | * original string, not an offset into its corresponding sequence of | ||
| 404 | * collation elements). The value returned by the next call to next() | ||
| 405 | * will be the collation element corresponding to the specified position | ||
| 406 | * in the text. If that position is in the middle of a contracting | ||
| 407 | * character sequence, the result of the next call to next() is the | ||
| 408 | * collation element for that sequence. This means that getOffset() | ||
| 409 | * is not guaranteed to return the same value as was passed to a preceding | ||
| 410 | * call to setOffset(). | ||
| 411 | * | ||
| 412 | * @param newOffset The new character offset into the original text. | ||
| 413 | * @since 1.2 | ||
| 414 | */ | ||
| 415 | public void setOffset(int newOffset) | ||
| 416 | { | ||
| 417 | if (text != null) { | ||
| 418 | if (newOffset < text.getBeginIndex() | ||
| 419 | || newOffset >= text.getEndIndex()) { | ||
| 420 | text.setIndexOnly(newOffset); | ||
| 421 | } else { | ||
| 422 | int c = text.setIndex(newOffset); | ||
| 423 | |||
| 424 | // if the desired character isn't used in a contracting character | ||
| 425 | // sequence, bypass all the backing-up logic-- we're sitting on | ||
| 426 | // the right character already | ||
| 427 | if (ordering.usedInContractSeq(c)) { | ||
| 428 | // walk backwards through the string until we see a character | ||
| 429 | // that DOESN'T participate in a contracting character sequence | ||
| 430 | while (ordering.usedInContractSeq(c)) { | ||
| 431 | c = text.previous(); | ||
| 432 | } | ||
| 433 | // now walk forward using this object's next() method until | ||
| 434 | // we pass the starting point and set our current position | ||
| 435 | // to the beginning of the last "character" before or at | ||
| 436 | // our starting position | ||
| 437 | int last = text.getIndex(); | ||
| 438 | while (text.getIndex() <= newOffset) { | ||
| 439 | last = text.getIndex(); | ||
| 440 | next(); | ||
| 441 | } | ||
| 442 | text.setIndexOnly(last); | ||
| 443 | // we don't need this, since last is the last index | ||
| 444 | // that is the starting of the contraction which encompass | ||
| 445 | // newOffset | ||
| 446 | // text.previous(); | ||
| 447 | } | ||
| 448 | } | ||
| 449 | } | ||
| 450 | buffer = null; | ||
| 451 | expIndex = 0; | ||
| 452 | swapOrder = 0; | ||
| 453 | } | ||
| 454 | |||
| 455 | /** | ||
| 456 | * Returns the character offset in the original text corresponding to the next | ||
| 457 | * collation element. (That is, getOffset() returns the position in the text | ||
| 458 | * corresponding to the collation element that will be returned by the next | ||
| 459 | * call to next().) This value will always be the index of the FIRST character | ||
| 460 | * corresponding to the collation element (a contracting character sequence is | ||
| 461 | * when two or more characters all correspond to the same collation element). | ||
| 462 | * This means if you do setOffset(x) followed immediately by getOffset(), getOffset() | ||
| 463 | * won't necessarily return x. | ||
| 464 | * | ||
| 465 | * @return The character offset in the original text corresponding to the collation | ||
| 466 | * element that will be returned by the next call to next(). | ||
| 467 | * @since 1.2 | ||
| 468 | */ | ||
| 469 | public int getOffset() | ||
| 470 | { | ||
| 471 | return (text != null) ? text.getIndex() : 0; | ||
| 472 | } | ||
| 473 | |||
| 474 | |||
| 475 | /** | ||
| 476 | * Return the maximum length of any expansion sequences that end | ||
| 477 | * with the specified comparison order. | ||
| 478 | * @param order a collation order returned by previous or next. | ||
| 479 | * @return the maximum length of any expansion sequences ending | ||
| 480 | * with the specified order. | ||
| 481 | * @since 1.2 | ||
| 482 | */ | ||
| 483 | public int getMaxExpansion(int order) | ||
| 484 | { | ||
| 485 | return ordering.getMaxExpansion(order); | ||
| 486 | } | ||
| 487 | |||
| 488 | /** | ||
| 489 | * Set a new string over which to iterate. | ||
| 490 | * | ||
| 491 | * @param source the new source text | ||
| 492 | * @since 1.2 | ||
| 493 | */ | ||
| 494 | public void setText(String source) | ||
| 495 | { | ||
| 496 | buffer = null; | ||
| 497 | swapOrder = 0; | ||
| 498 | expIndex = 0; | ||
| 499 | NormalizerBase.Mode mode = | ||
| 500 | CollatorUtilities.toNormalizerMode(owner.getDecomposition()); | ||
| 501 | if (text == null) { | ||
| 502 | text = new NormalizerBase(source, mode); | ||
| 503 | } else { | ||
| 504 | text.setMode(mode); | ||
| 505 | text.setText(source); | ||
| 506 | } | ||
| 507 | } | ||
| 508 | |||
| 509 | /** | ||
| 510 | * Set a new string over which to iterate. | ||
| 511 | * | ||
| 512 | * @param source the new source text. | ||
| 513 | * @since 1.2 | ||
| 514 | */ | ||
| 515 | public void setText(CharacterIterator source) | ||
| 516 | { | ||
| 517 | buffer = null; | ||
| 518 | swapOrder = 0; | ||
| 519 | expIndex = 0; | ||
| 520 | NormalizerBase.Mode mode = | ||
| 521 | CollatorUtilities.toNormalizerMode(owner.getDecomposition()); | ||
| 522 | if (text == null) { | ||
| 523 | text = new NormalizerBase(source, mode); | ||
| 524 | } else { | ||
| 525 | text.setMode(mode); | ||
| 526 | text.setText(source); | ||
| 527 | } | ||
| 528 | } | ||
| 529 | |||
| 530 | //============================================================ | ||
| 531 | // privates | ||
| 532 | //============================================================ | ||
| 533 | |||
| 534 | /** | ||
| 535 | * Determine if a character is a Thai vowel (which sorts after | ||
| 536 | * its base consonant). | ||
| 537 | */ | ||
| 538 | private final static boolean isThaiPreVowel(int ch) { | ||
| 539 | return (ch >= 0x0e40) && (ch <= 0x0e44); | ||
| 540 | } | ||
| 541 | |||
| 542 | /** | ||
| 543 | * Determine if a character is a Thai base consonant | ||
| 544 | */ | ||
| 545 | private final static boolean isThaiBaseConsonant(int ch) { | ||
| 546 | return (ch >= 0x0e01) && (ch <= 0x0e2e); | ||
| 547 | } | ||
| 548 | |||
| 549 | /** | ||
| 550 | * Determine if a character is a Lao vowel (which sorts after | ||
| 551 | * its base consonant). | ||
| 552 | */ | ||
| 553 | private final static boolean isLaoPreVowel(int ch) { | ||
| 554 | return (ch >= 0x0ec0) && (ch <= 0x0ec4); | ||
| 555 | } | ||
| 556 | |||
| 557 | /** | ||
| 558 | * Determine if a character is a Lao base consonant | ||
| 559 | */ | ||
| 560 | private final static boolean isLaoBaseConsonant(int ch) { | ||
| 561 | return (ch >= 0x0e81) && (ch <= 0x0eae); | ||
| 562 | } | ||
| 563 | |||
| 564 | /** | ||
| 565 | * This method produces a buffer which contains the collation | ||
| 566 | * elements for the two characters, with colFirst's values preceding | ||
| 567 | * another character's. Presumably, the other character precedes colFirst | ||
| 568 | * in logical order (otherwise you wouldn't need this method would you?). | ||
| 569 | * The assumption is that the other char's value(s) have already been | ||
| 570 | * computed. If this char has a single element it is passed to this | ||
| 571 | * method as lastValue, and lastExpansion is null. If it has an | ||
| 572 | * expansion it is passed in lastExpansion, and colLastValue is ignored. | ||
| 573 | */ | ||
| 574 | private int[] makeReorderedBuffer(int colFirst, | ||
| 575 | int lastValue, | ||
| 576 | int[] lastExpansion, | ||
| 577 | boolean forward) { | ||
| 578 | |||
| 579 | int[] result; | ||
| 580 | |||
| 581 | int firstValue = ordering.getUnicodeOrder(colFirst); | ||
| 582 | if (firstValue >= RuleBasedCollator.CONTRACTCHARINDEX) { | ||
| 583 | firstValue = forward? nextContractChar(colFirst) : prevContractChar(colFirst); | ||
| 584 | } | ||
| 585 | |||
| 586 | int[] firstExpansion = null; | ||
| 587 | if (firstValue >= RuleBasedCollator.EXPANDCHARINDEX) { | ||
| 588 | firstExpansion = ordering.getExpandValueList(firstValue); | ||
| 589 | } | ||
| 590 | |||
| 591 | if (!forward) { | ||
| 592 | int temp1 = firstValue; | ||
| 593 | firstValue = lastValue; | ||
| 594 | lastValue = temp1; | ||
| 595 | int[] temp2 = firstExpansion; | ||
| 596 | firstExpansion = lastExpansion; | ||
| 597 | lastExpansion = temp2; | ||
| 598 | } | ||
| 599 | |||
| 600 | if (firstExpansion == null && lastExpansion == null) { | ||
| 601 | result = new int [2]; | ||
| 602 | result[0] = firstValue; | ||
| 603 | result[1] = lastValue; | ||
| 604 | } | ||
| 605 | else { | ||
| 606 | int firstLength = firstExpansion==null? 1 : firstExpansion.length; | ||
| 607 | int lastLength = lastExpansion==null? 1 : lastExpansion.length; | ||
| 608 | result = new int[firstLength + lastLength]; | ||
| 609 | |||
| 610 | if (firstExpansion == null) { | ||
| 611 | result[0] = firstValue; | ||
| 612 | } | ||
| 613 | else { | ||
| 614 | System.arraycopy(firstExpansion, 0, result, 0, firstLength); | ||
| 615 | } | ||
| 616 | |||
| 617 | if (lastExpansion == null) { | ||
| 618 | result[firstLength] = lastValue; | ||
| 619 | } | ||
| 620 | else { | ||
| 621 | System.arraycopy(lastExpansion, 0, result, firstLength, lastLength); | ||
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | return result; | ||
| 626 | } | ||
| 627 | |||
| 628 | /** | ||
| 629 | * Check if a comparison order is ignorable. | ||
| 630 | * @return true if a character is ignorable, false otherwise. | ||
| 631 | */ | ||
| 632 | final static boolean isIgnorable(int order) | ||
| 633 | { | ||
| 634 | return ((primaryOrder(order) == 0) ? true : false); | ||
| 635 | } | ||
| 636 | |||
| 637 | /** | ||
| 638 | * Get the ordering priority of the next contracting character in the | ||
| 639 | * string. | ||
| 640 | * @param ch the starting character of a contracting character token | ||
| 641 | * @return the next contracting character's ordering. Returns NULLORDER | ||
| 642 | * if the end of string is reached. | ||
| 643 | */ | ||
| 644 | private int nextContractChar(int ch) | ||
| 645 | { | ||
| 646 | // First get the ordering of this single character, | ||
| 647 | // which is always the first element in the list | ||
| 648 | Vector list = ordering.getContractValues(ch); | ||
| 649 | EntryPair pair = (EntryPair)list.firstElement(); | ||
| 650 | int order = pair.value; | ||
| 651 | |||
| 652 | // find out the length of the longest contracting character sequence in the list. | ||
| 653 | // There's logic in the builder code to make sure the longest sequence is always | ||
| 654 | // the last. | ||
| 655 | pair = (EntryPair)list.lastElement(); | ||
| 656 | int maxLength = pair.entryName.length(); | ||
| 657 | |||
| 658 | // (the Normalizer is cloned here so that the seeking we do in the next loop | ||
| 659 | // won't affect our real position in the text) | ||
| 660 | NormalizerBase tempText = (NormalizerBase)text.clone(); | ||
| 661 | |||
| 662 | // extract the next maxLength characters in the string (we have to do this using the | ||
| 663 | // Normalizer to ensure that our offsets correspond to those the rest of the | ||
| 664 | // iterator is using) and store it in "fragment". | ||
| 665 | tempText.previous(); | ||
| 666 | key.setLength(0); | ||
| 667 | int c = tempText.next(); | ||
| 668 | while (maxLength > 0 && c != NormalizerBase.DONE) { | ||
| 669 | if (Character.isSupplementaryCodePoint(c)) { | ||
| 670 | key.append(Character.toChars(c)); | ||
| 671 | maxLength -= 2; | ||
| 672 | } else { | ||
| 673 | key.append((char)c); | ||
| 674 | --maxLength; | ||
| 675 | } | ||
| 676 | c = tempText.next(); | ||
| 677 | } | ||
| 678 | String fragment = key.toString(); | ||
| 679 | // now that we have that fragment, iterate through this list looking for the | ||
| 680 | // longest sequence that matches the characters in the actual text. (maxLength | ||
| 681 | // is used here to keep track of the length of the longest sequence) | ||
| 682 | // Upon exit from this loop, maxLength will contain the length of the matching | ||
| 683 | // sequence and order will contain the collation-element value corresponding | ||
| 684 | // to this sequence | ||
| 685 | maxLength = 1; | ||
| 686 | for (int i = list.size() - 1; i > 0; i--) { | ||
| 687 | pair = (EntryPair)list.elementAt(i); | ||
| 688 | if (!pair.fwd) | ||
| 689 | continue; | ||
| 690 | |||
| 691 | if (fragment.startsWith(pair.entryName) && pair.entryName.length() | ||
| 692 | > maxLength) { | ||
| 693 | maxLength = pair.entryName.length(); | ||
| 694 | order = pair.value; | ||
| 695 | } | ||
| 696 | } | ||
| 697 | |||
| 698 | // seek our current iteration position to the end of the matching sequence | ||
| 699 | // and return the appropriate collation-element value (if there was no matching | ||
| 700 | // sequence, we're already seeked to the right position and order already contains | ||
| 701 | // the correct collation-element value for the single character) | ||
| 702 | while (maxLength > 1) { | ||
| 703 | c = text.next(); | ||
| 704 | maxLength -= Character.charCount(c); | ||
| 705 | } | ||
| 706 | return order; | ||
| 707 | } | ||
| 708 | |||
| 709 | /** | ||
| 710 | * Get the ordering priority of the previous contracting character in the | ||
| 711 | * string. | ||
| 712 | * @param ch the starting character of a contracting character token | ||
| 713 | * @return the next contracting character's ordering. Returns NULLORDER | ||
| 714 | * if the end of string is reached. | ||
| 715 | */ | ||
| 716 | private int prevContractChar(int ch) | ||
| 717 | { | ||
| 718 | // This function is identical to nextContractChar(), except that we've | ||
| 719 | // switched things so that the next() and previous() calls on the Normalizer | ||
| 720 | // are switched and so that we skip entry pairs with the fwd flag turned on | ||
| 721 | // rather than off. Notice that we still use append() and startsWith() when | ||
| 722 | // working on the fragment. This is because the entry pairs that are used | ||
| 723 | // in reverse iteration have their names reversed already. | ||
| 724 | Vector list = ordering.getContractValues(ch); | ||
| 725 | EntryPair pair = (EntryPair)list.firstElement(); | ||
| 726 | int order = pair.value; | ||
| 727 | |||
| 728 | pair = (EntryPair)list.lastElement(); | ||
| 729 | int maxLength = pair.entryName.length(); | ||
| 730 | |||
| 731 | NormalizerBase tempText = (NormalizerBase)text.clone(); | ||
| 732 | |||
| 733 | tempText.next(); | ||
| 734 | key.setLength(0); | ||
| 735 | int c = tempText.previous(); | ||
| 736 | while (maxLength > 0 && c != NormalizerBase.DONE) { | ||
| 737 | if (Character.isSupplementaryCodePoint(c)) { | ||
| 738 | key.append(Character.toChars(c)); | ||
| 739 | maxLength -= 2; | ||
| 740 | } else { | ||
| 741 | key.append((char)c); | ||
| 742 | --maxLength; | ||
| 743 | } | ||
| 744 | c = tempText.previous(); | ||
| 745 | } | ||
| 746 | String fragment = key.toString(); | ||
| 747 | |||
| 748 | maxLength = 1; | ||
| 749 | for (int i = list.size() - 1; i > 0; i--) { | ||
| 750 | pair = (EntryPair)list.elementAt(i); | ||
| 751 | if (pair.fwd) | ||
| 752 | continue; | ||
| 753 | |||
| 754 | if (fragment.startsWith(pair.entryName) && pair.entryName.length() | ||
| 755 | > maxLength) { | ||
| 756 | maxLength = pair.entryName.length(); | ||
| 757 | order = pair.value; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | while (maxLength > 1) { | ||
| 762 | c = text.previous(); | ||
| 763 | maxLength -= Character.charCount(c); | ||
| 764 | } | ||
| 765 | return order; | ||
| 766 | } | ||
| 767 | |||
| 768 | final static int UNMAPPEDCHARVALUE = 0x7FFF0000; | ||
| 769 | |||
| 770 | private NormalizerBase text = null; | ||
| 771 | private int[] buffer = null; | ||
| 772 | private int expIndex = 0; | ||
| 773 | private StringBuffer key = new StringBuffer(5); | ||
| 774 | private int swapOrder = 0; | ||
| 775 | private RBCollationTables ordering; | ||
| 776 | private RuleBasedCollator owner; | ||
| 777 | } | ||