1 /* 2 * Copyright (c) 2000, 2018, 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 package java.awt.font; 27 28 import java.io.IOException; 29 import java.io.ObjectOutputStream; 30 import java.util.Arrays; 31 import java.util.Comparator; 32 import java.util.EnumSet; 33 import java.util.Set; 34 import jdk.internal.access.SharedSecrets; 35 36 /** 37 * The {@code NumericShaper} class is used to convert Latin-1 (European) 38 * digits to other Unicode decimal digits. Users of this class will 39 * primarily be people who wish to present data using 40 * national digit shapes, but find it more convenient to represent the 41 * data internally using Latin-1 (European) digits. This does not 42 * interpret the deprecated numeric shape selector character (U+206E). 43 * <p> 44 * Instances of {@code NumericShaper} are typically applied 45 * as attributes to text with the 46 * {@link TextAttribute#NUMERIC_SHAPING NUMERIC_SHAPING} attribute 47 * of the {@code TextAttribute} class. 48 * For example, this code snippet causes a {@code TextLayout} to 49 * shape European digits to Arabic in an Arabic context:<br> 50 * <blockquote><pre> 51 * Map map = new HashMap(); 52 * map.put(TextAttribute.NUMERIC_SHAPING, 53 * NumericShaper.getContextualShaper(NumericShaper.ARABIC)); 54 * FontRenderContext frc = ...; 55 * TextLayout layout = new TextLayout(text, map, frc); 56 * layout.draw(g2d, x, y); 57 * </pre></blockquote> 58 * <br> 59 * It is also possible to perform numeric shaping explicitly using instances 60 * of {@code NumericShaper}, as this code snippet demonstrates:<br> 61 * <blockquote><pre> 62 * char[] text = ...; 63 * // shape all EUROPEAN digits (except zero) to ARABIC digits 64 * NumericShaper shaper = NumericShaper.getShaper(NumericShaper.ARABIC); 65 * shaper.shape(text, start, count); 66 * 67 * // shape European digits to ARABIC digits if preceding text is Arabic, or 68 * // shape European digits to TAMIL digits if preceding text is Tamil, or 69 * // leave European digits alone if there is no preceding text, or 70 * // preceding text is neither Arabic nor Tamil 71 * NumericShaper shaper = 72 * NumericShaper.getContextualShaper(NumericShaper.ARABIC | 73 * NumericShaper.TAMIL, 74 * NumericShaper.EUROPEAN); 75 * shaper.shape(text, start, count); 76 * </pre></blockquote> 77 * 78 * <p><b>Bit mask- and enum-based Unicode ranges</b></p> 79 * 80 * <p>This class supports two different programming interfaces to 81 * represent Unicode ranges for script-specific digits: bit 82 * mask-based ones, such as {@link #ARABIC NumericShaper.ARABIC}, and 83 * enum-based ones, such as {@link NumericShaper.Range#ARABIC}. 84 * Multiple ranges can be specified by ORing bit mask-based constants, 85 * such as: 86 * <blockquote><pre> 87 * NumericShaper.ARABIC | NumericShaper.TAMIL 88 * </pre></blockquote> 89 * or creating a {@code Set} with the {@link NumericShaper.Range} 90 * constants, such as: 91 * <blockquote><pre> 92 * EnumSet.of(NumericShaper.Range.ARABIC, NumericShaper.Range.TAMIL) 93 * </pre></blockquote> 94 * The enum-based ranges are a super set of the bit mask-based ones. 95 * 96 * <p>If the two interfaces are mixed (including serialization), 97 * Unicode range values are mapped to their counterparts where such 98 * mapping is possible, such as {@code NumericShaper.Range.ARABIC} 99 * from/to {@code NumericShaper.ARABIC}. If any unmappable range 100 * values are specified, such as {@code NumericShaper.Range.BALINESE}, 101 * those ranges are ignored. 102 * 103 * <p><b>Decimal Digits Precedence</b></p> 104 * 105 * <p>A Unicode range may have more than one set of decimal digits. If 106 * multiple decimal digits sets are specified for the same Unicode 107 * range, one of the sets will take precedence as follows. 108 * 109 * <table class="plain"> 110 * <caption>NumericShaper constants precedence</caption> 111 * <thead> 112 * <tr> 113 * <th scope="col">Unicode Range 114 * <th scope="col">{@code NumericShaper} Constants 115 * <th scope="col">Precedence 116 * </thead> 117 * <tbody> 118 * <tr> 119 * <th scope="row" rowspan="2">Arabic 120 * <td>{@link NumericShaper#ARABIC NumericShaper.ARABIC} 121 * <br> 122 * {@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC} 123 * <td>{@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC} 124 * </tr> 125 * <tr> 126 * <th scope="row">{@link NumericShaper.Range#ARABIC} 127 * <br> 128 * {@link NumericShaper.Range#EASTERN_ARABIC} 129 * <td>{@link NumericShaper.Range#EASTERN_ARABIC} 130 * <tr> 131 * <th scope="row">Tai Tham 132 * <td>{@link NumericShaper.Range#TAI_THAM_HORA} 133 * <br> 134 * {@link NumericShaper.Range#TAI_THAM_THAM} 135 * <td>{@link NumericShaper.Range#TAI_THAM_THAM} 136 * </tbody> 137 * </table> 138 * 139 * @since 1.4 140 */ 141 public final class NumericShaper implements java.io.Serializable { 142 143 // For access from java.text.Bidi 144 static { 145 if (SharedSecrets.getJavaAWTFontAccess() == null) { 146 SharedSecrets.setJavaAWTFontAccess(new JavaAWTFontAccessImpl()); 147 } 148 } 149 150 /** 151 * A {@code NumericShaper.Range} represents a Unicode range of a 152 * script having its own decimal digits. For example, the {@link 153 * NumericShaper.Range#THAI} range has the Thai digits, THAI DIGIT 154 * ZERO (U+0E50) to THAI DIGIT NINE (U+0E59). 155 * 156 * <p>The {@code Range} enum replaces the traditional bit 157 * mask-based values (e.g., {@link NumericShaper#ARABIC}), and 158 * supports more Unicode ranges than the bit mask-based ones. For 159 * example, the following code using the bit mask: 160 * <blockquote><pre> 161 * NumericShaper.getContextualShaper(NumericShaper.ARABIC | 162 * NumericShaper.TAMIL, 163 * NumericShaper.EUROPEAN); 164 * </pre></blockquote> 165 * can be written using this enum as: 166 * <blockquote><pre> 167 * NumericShaper.getContextualShaper(EnumSet.of( 168 * NumericShaper.Range.ARABIC, 169 * NumericShaper.Range.TAMIL), 170 * NumericShaper.Range.EUROPEAN); 171 * </pre></blockquote> 172 * 173 * @since 1.7 174 */ 175 public static enum Range { 176 // The order of EUROPEAN to MOGOLIAN must be consistent 177 // with the bitmask-based constants. 178 /** 179 * The Latin (European) range with the Latin (ASCII) digits. 180 */ 181 EUROPEAN ('\u0030', '\u0000', '\u0300'), 182 /** 183 * The Arabic range with the Arabic-Indic digits. 184 */ 185 ARABIC ('\u0660', '\u0600', '\u0780'), 186 /** 187 * The Arabic range with the Eastern Arabic-Indic digits. 188 */ 189 EASTERN_ARABIC ('\u06f0', '\u0600', '\u0780'), 190 /** 191 * The Devanagari range with the Devanagari digits. 192 */ 193 DEVANAGARI ('\u0966', '\u0900', '\u0980'), 194 /** 195 * The Bengali range with the Bengali digits. 196 */ 197 BENGALI ('\u09e6', '\u0980', '\u0a00'), 198 /** 199 * The Gurmukhi range with the Gurmukhi digits. 200 */ 201 GURMUKHI ('\u0a66', '\u0a00', '\u0a80'), 202 /** 203 * The Gujarati range with the Gujarati digits. 204 */ 205 GUJARATI ('\u0ae6', '\u0b00', '\u0b80'), 206 /** 207 * The Oriya range with the Oriya digits. 208 */ 209 ORIYA ('\u0b66', '\u0b00', '\u0b80'), 210 /** 211 * The Tamil range with the Tamil digits. 212 */ 213 TAMIL ('\u0be6', '\u0b80', '\u0c00'), 214 /** 215 * The Telugu range with the Telugu digits. 216 */ 217 TELUGU ('\u0c66', '\u0c00', '\u0c80'), 218 /** 219 * The Kannada range with the Kannada digits. 220 */ 221 KANNADA ('\u0ce6', '\u0c80', '\u0d00'), 222 /** 223 * The Malayalam range with the Malayalam digits. 224 */ 225 MALAYALAM ('\u0d66', '\u0d00', '\u0d80'), 226 /** 227 * The Thai range with the Thai digits. 228 */ 229 THAI ('\u0e50', '\u0e00', '\u0e80'), 230 /** 231 * The Lao range with the Lao digits. 232 */ 233 LAO ('\u0ed0', '\u0e80', '\u0f00'), 234 /** 235 * The Tibetan range with the Tibetan digits. 236 */ 237 TIBETAN ('\u0f20', '\u0f00', '\u1000'), 238 /** 239 * The Myanmar range with the Myanmar digits. 240 */ 241 MYANMAR ('\u1040', '\u1000', '\u1080'), 242 /** 243 * The Ethiopic range with the Ethiopic digits. Ethiopic 244 * does not have a decimal digit 0 so Latin (European) 0 is 245 * used. 246 */ 247 ETHIOPIC ('\u1369', '\u1200', '\u1380') { 248 @Override 249 char getNumericBase() { return 1; } 250 }, 251 /** 252 * The Khmer range with the Khmer digits. 253 */ 254 KHMER ('\u17e0', '\u1780', '\u1800'), 255 /** 256 * The Mongolian range with the Mongolian digits. 257 */ 258 MONGOLIAN ('\u1810', '\u1800', '\u1900'), 259 // The order of EUROPEAN to MOGOLIAN must be consistent 260 // with the bitmask-based constants. 261 262 /** 263 * The N'Ko range with the N'Ko digits. 264 */ 265 NKO ('\u07c0', '\u07c0', '\u0800'), 266 /** 267 * The Myanmar range with the Myanmar Shan digits. 268 */ 269 MYANMAR_SHAN ('\u1090', '\u1000', '\u10a0'), 270 /** 271 * The Limbu range with the Limbu digits. 272 */ 273 LIMBU ('\u1946', '\u1900', '\u1950'), 274 /** 275 * The New Tai Lue range with the New Tai Lue digits. 276 */ 277 NEW_TAI_LUE ('\u19d0', '\u1980', '\u19e0'), 278 /** 279 * The Balinese range with the Balinese digits. 280 */ 281 BALINESE ('\u1b50', '\u1b00', '\u1b80'), 282 /** 283 * The Sundanese range with the Sundanese digits. 284 */ 285 SUNDANESE ('\u1bb0', '\u1b80', '\u1bc0'), 286 /** 287 * The Lepcha range with the Lepcha digits. 288 */ 289 LEPCHA ('\u1c40', '\u1c00', '\u1c50'), 290 /** 291 * The Ol Chiki range with the Ol Chiki digits. 292 */ 293 OL_CHIKI ('\u1c50', '\u1c50', '\u1c80'), 294 /** 295 * The Vai range with the Vai digits. 296 */ 297 VAI ('\ua620', '\ua500', '\ua640'), 298 /** 299 * The Saurashtra range with the Saurashtra digits. 300 */ 301 SAURASHTRA ('\ua8d0', '\ua880', '\ua8e0'), 302 /** 303 * The Kayah Li range with the Kayah Li digits. 304 */ 305 KAYAH_LI ('\ua900', '\ua900', '\ua930'), 306 /** 307 * The Cham range with the Cham digits. 308 */ 309 CHAM ('\uaa50', '\uaa00', '\uaa60'), 310 /** 311 * The Tai Tham Hora range with the Tai Tham Hora digits. 312 */ 313 TAI_THAM_HORA ('\u1a80', '\u1a20', '\u1ab0'), 314 /** 315 * The Tai Tham Tham range with the Tai Tham Tham digits. 316 */ 317 TAI_THAM_THAM ('\u1a90', '\u1a20', '\u1ab0'), 318 /** 319 * The Javanese range with the Javanese digits. 320 */ 321 JAVANESE ('\ua9d0', '\ua980', '\ua9e0'), 322 /** 323 * The Meetei Mayek range with the Meetei Mayek digits. 324 */ 325 MEETEI_MAYEK ('\uabf0', '\uabc0', '\uac00'), 326 /** 327 * The Sinhala range with the Sinhala digits. 328 * @since 9 329 */ 330 SINHALA ('\u0de6', '\u0d80', '\u0e00'), 331 /** 332 * The Myanmar Extended-B range with the Myanmar Tai Laing digits. 333 * @since 9 334 */ 335 MYANMAR_TAI_LAING ('\ua9f0', '\ua9e0', '\uaa00'); 336 337 private static int toRangeIndex(Range script) { 338 int index = script.ordinal(); 339 return index < NUM_KEYS ? index : -1; 340 } 341 342 private static Range indexToRange(int index) { 343 return index < NUM_KEYS ? Range.values()[index] : null; 344 } 345 346 private static int toRangeMask(Set<Range> ranges) { 347 int m = 0; 348 for (Range range : ranges) { 349 int index = range.ordinal(); 350 if (index < NUM_KEYS) { 351 m |= 1 << index; 352 } 353 } 354 return m; 355 } 356 357 private static Set<Range> maskToRangeSet(int mask) { 358 Set<Range> set = EnumSet.noneOf(Range.class); 359 Range[] a = Range.values(); 360 for (int i = 0; i < NUM_KEYS; i++) { 361 if ((mask & (1 << i)) != 0) { 362 set.add(a[i]); 363 } 364 } 365 return set; 366 } 367 368 // base character of range digits 369 private final int base; 370 // Unicode range 371 private final int start, // inclusive 372 end; // exclusive 373 374 private Range(int base, int start, int end) { 375 this.base = base - ('0' + getNumericBase()); 376 this.start = start; 377 this.end = end; 378 } 379 380 private int getDigitBase() { 381 return base; 382 } 383 384 char getNumericBase() { 385 return 0; 386 } 387 388 private boolean inRange(int c) { 389 return start <= c && c < end; 390 } 391 } 392 393 /** index of context for contextual shaping - values range from 0 to 18 */ 394 private int key; 395 396 /** flag indicating whether to shape contextually (high bit) and which 397 * digit ranges to shape (bits 0-18) 398 */ 399 private int mask; 400 401 /** 402 * The context {@code Range} for contextual shaping or the {@code 403 * Range} for non-contextual shaping. {@code null} for the bit 404 * mask-based API. 405 * 406 * @since 1.7 407 */ 408 private Range shapingRange; 409 410 /** 411 * {@code Set<Range>} indicating which Unicode ranges to 412 * shape. {@code null} for the bit mask-based API. 413 */ 414 private transient Set<Range> rangeSet; 415 416 /** 417 * rangeSet.toArray() value. Sorted by Range.base when the number 418 * of elements is greater than BSEARCH_THRESHOLD. 419 */ 420 private transient Range[] rangeArray; 421 422 /** 423 * If more than BSEARCH_THRESHOLD ranges are specified, binary search is used. 424 */ 425 private static final int BSEARCH_THRESHOLD = 3; 426 427 private static final long serialVersionUID = -8022764705923730308L; 428 429 /** Identifies the Latin-1 (European) and extended range, and 430 * Latin-1 (European) decimal base. 431 */ 432 public static final int EUROPEAN = 1<<0; 433 434 /** Identifies the ARABIC range and decimal base. */ 435 public static final int ARABIC = 1<<1; 436 437 /** Identifies the ARABIC range and ARABIC_EXTENDED decimal base. */ 438 public static final int EASTERN_ARABIC = 1<<2; 439 440 /** Identifies the DEVANAGARI range and decimal base. */ 441 public static final int DEVANAGARI = 1<<3; 442 443 /** Identifies the BENGALI range and decimal base. */ 444 public static final int BENGALI = 1<<4; 445 446 /** Identifies the GURMUKHI range and decimal base. */ 447 public static final int GURMUKHI = 1<<5; 448 449 /** Identifies the GUJARATI range and decimal base. */ 450 public static final int GUJARATI = 1<<6; 451 452 /** Identifies the ORIYA range and decimal base. */ 453 public static final int ORIYA = 1<<7; 454 455 /** Identifies the TAMIL range and decimal base. */ 456 // TAMIL DIGIT ZERO was added in Unicode 4.1 457 public static final int TAMIL = 1<<8; 458 459 /** Identifies the TELUGU range and decimal base. */ 460 public static final int TELUGU = 1<<9; 461 462 /** Identifies the KANNADA range and decimal base. */ 463 public static final int KANNADA = 1<<10; 464 465 /** Identifies the MALAYALAM range and decimal base. */ 466 public static final int MALAYALAM = 1<<11; 467 468 /** Identifies the THAI range and decimal base. */ 469 public static final int THAI = 1<<12; 470 471 /** Identifies the LAO range and decimal base. */ 472 public static final int LAO = 1<<13; 473 474 /** Identifies the TIBETAN range and decimal base. */ 475 public static final int TIBETAN = 1<<14; 476 477 /** Identifies the MYANMAR range and decimal base. */ 478 public static final int MYANMAR = 1<<15; 479 480 /** Identifies the ETHIOPIC range and decimal base. */ 481 public static final int ETHIOPIC = 1<<16; 482 483 /** Identifies the KHMER range and decimal base. */ 484 public static final int KHMER = 1<<17; 485 486 /** Identifies the MONGOLIAN range and decimal base. */ 487 public static final int MONGOLIAN = 1<<18; 488 489 /** Identifies all ranges, for full contextual shaping. 490 * 491 * <p>This constant specifies all of the bit mask-based 492 * ranges. Use {@code EnumSet.allOf(NumericShaper.Range.class)} to 493 * specify all of the enum-based ranges. 494 */ 495 public static final int ALL_RANGES = 0x0007ffff; 496 497 private static final int EUROPEAN_KEY = 0; 498 private static final int ARABIC_KEY = 1; 499 private static final int EASTERN_ARABIC_KEY = 2; 500 private static final int DEVANAGARI_KEY = 3; 501 private static final int BENGALI_KEY = 4; 502 private static final int GURMUKHI_KEY = 5; 503 private static final int GUJARATI_KEY = 6; 504 private static final int ORIYA_KEY = 7; 505 private static final int TAMIL_KEY = 8; 506 private static final int TELUGU_KEY = 9; 507 private static final int KANNADA_KEY = 10; 508 private static final int MALAYALAM_KEY = 11; 509 private static final int THAI_KEY = 12; 510 private static final int LAO_KEY = 13; 511 private static final int TIBETAN_KEY = 14; 512 private static final int MYANMAR_KEY = 15; 513 private static final int ETHIOPIC_KEY = 16; 514 private static final int KHMER_KEY = 17; 515 private static final int MONGOLIAN_KEY = 18; 516 517 private static final int NUM_KEYS = MONGOLIAN_KEY + 1; // fixed 518 519 private static final int CONTEXTUAL_MASK = 1<<31; 520 521 private static final char[] bases = { 522 '\u0030' - '\u0030', // EUROPEAN 523 '\u0660' - '\u0030', // ARABIC-INDIC 524 '\u06f0' - '\u0030', // EXTENDED ARABIC-INDIC (EASTERN_ARABIC) 525 '\u0966' - '\u0030', // DEVANAGARI 526 '\u09e6' - '\u0030', // BENGALI 527 '\u0a66' - '\u0030', // GURMUKHI 528 '\u0ae6' - '\u0030', // GUJARATI 529 '\u0b66' - '\u0030', // ORIYA 530 '\u0be6' - '\u0030', // TAMIL - zero was added in Unicode 4.1 531 '\u0c66' - '\u0030', // TELUGU 532 '\u0ce6' - '\u0030', // KANNADA 533 '\u0d66' - '\u0030', // MALAYALAM 534 '\u0e50' - '\u0030', // THAI 535 '\u0ed0' - '\u0030', // LAO 536 '\u0f20' - '\u0030', // TIBETAN 537 '\u1040' - '\u0030', // MYANMAR 538 '\u1369' - '\u0031', // ETHIOPIC - no zero 539 '\u17e0' - '\u0030', // KHMER 540 '\u1810' - '\u0030', // MONGOLIAN 541 }; 542 543 // some ranges adjoin or overlap, rethink if we want to do a binary search on this 544 545 private static final char[] contexts = { 546 '\u0000', '\u0300', // 'EUROPEAN' (really latin-1 and extended) 547 '\u0600', '\u0780', // ARABIC 548 '\u0600', '\u0780', // EASTERN_ARABIC -- note overlap with arabic 549 '\u0900', '\u0980', // DEVANAGARI 550 '\u0980', '\u0a00', // BENGALI 551 '\u0a00', '\u0a80', // GURMUKHI 552 '\u0a80', '\u0b00', // GUJARATI 553 '\u0b00', '\u0b80', // ORIYA 554 '\u0b80', '\u0c00', // TAMIL 555 '\u0c00', '\u0c80', // TELUGU 556 '\u0c80', '\u0d00', // KANNADA 557 '\u0d00', '\u0d80', // MALAYALAM 558 '\u0e00', '\u0e80', // THAI 559 '\u0e80', '\u0f00', // LAO 560 '\u0f00', '\u1000', // TIBETAN 561 '\u1000', '\u1080', // MYANMAR 562 '\u1200', '\u1380', // ETHIOPIC - note missing zero 563 '\u1780', '\u1800', // KHMER 564 '\u1800', '\u1900', // MONGOLIAN 565 '\uffff', 566 }; 567 568 // assume most characters are near each other so probing the cache is infrequent, 569 // and a linear probe is ok. 570 571 private static int ctCache = 0; 572 private static int ctCacheLimit = contexts.length - 2; 573 574 // warning, synchronize access to this as it modifies state 575 private static int getContextKey(char c) { 576 if (c < contexts[ctCache]) { 577 while (ctCache > 0 && c < contexts[ctCache]) --ctCache; 578 } else if (c >= contexts[ctCache + 1]) { 579 while (ctCache < ctCacheLimit && c >= contexts[ctCache + 1]) ++ctCache; 580 } 581 582 // if we're not in a known range, then return EUROPEAN as the range key 583 return (ctCache & 0x1) == 0 ? (ctCache / 2) : EUROPEAN_KEY; 584 } 585 586 // cache for the NumericShaper.Range version 587 private transient volatile Range currentRange = Range.EUROPEAN; 588 589 private Range rangeForCodePoint(final int codepoint) { 590 if (currentRange.inRange(codepoint)) { 591 return currentRange; 592 } 593 594 final Range[] ranges = rangeArray; 595 if (ranges.length > BSEARCH_THRESHOLD) { 596 int lo = 0; 597 int hi = ranges.length - 1; 598 while (lo <= hi) { 599 int mid = (lo + hi) / 2; 600 Range range = ranges[mid]; 601 if (codepoint < range.start) { 602 hi = mid - 1; 603 } else if (codepoint >= range.end) { 604 lo = mid + 1; 605 } else { 606 currentRange = range; 607 return range; 608 } 609 } 610 } else { 611 for (int i = 0; i < ranges.length; i++) { 612 if (ranges[i].inRange(codepoint)) { 613 return ranges[i]; 614 } 615 } 616 } 617 return Range.EUROPEAN; 618 } 619 620 /* 621 * A range table of strong directional characters (types L, R, AL). 622 * Even (left) indexes are starts of ranges of non-strong-directional (or undefined) 623 * characters, odd (right) indexes are starts of ranges of strong directional 624 * characters. 625 */ 626 private static int[] strongTable = { 627 0x0000, 0x0041, 628 0x005b, 0x0061, 629 0x007b, 0x00aa, 630 0x00ab, 0x00b5, 631 0x00b6, 0x00ba, 632 0x00bb, 0x00c0, 633 0x00d7, 0x00d8, 634 0x00f7, 0x00f8, 635 0x02b9, 0x02bb, 636 0x02c2, 0x02d0, 637 0x02d2, 0x02e0, 638 0x02e5, 0x02ee, 639 0x02ef, 0x0370, 640 0x0374, 0x0376, 641 0x0378, 0x037a, 642 0x037e, 0x037f, 643 0x0380, 0x0386, 644 0x0387, 0x0388, 645 0x038b, 0x038c, 646 0x038d, 0x038e, 647 0x03a2, 0x03a3, 648 0x03f6, 0x03f7, 649 0x0483, 0x048a, 650 0x0530, 0x0531, 651 0x0557, 0x0559, 652 0x058a, 0x0590, 653 0x0591, 0x05be, 654 0x05bf, 0x05c0, 655 0x05c1, 0x05c3, 656 0x05c4, 0x05c6, 657 0x05c7, 0x05c8, 658 0x0600, 0x0608, 659 0x0609, 0x060b, 660 0x060c, 0x060d, 661 0x060e, 0x061b, 662 0x064b, 0x066d, 663 0x0670, 0x0671, 664 0x06d6, 0x06e5, 665 0x06e7, 0x06ee, 666 0x06f0, 0x06fa, 667 0x0711, 0x0712, 668 0x0730, 0x074b, 669 0x07a6, 0x07b1, 670 0x07eb, 0x07f4, 671 0x07f6, 0x07fa, 672 0x07fd, 0x07fe, 673 0x0816, 0x081a, 674 0x081b, 0x0824, 675 0x0825, 0x0828, 676 0x0829, 0x082e, 677 0x0859, 0x085c, 678 0x08e3, 0x0903, 679 0x093a, 0x093b, 680 0x093c, 0x093d, 681 0x0941, 0x0949, 682 0x094d, 0x094e, 683 0x0951, 0x0958, 684 0x0962, 0x0964, 685 0x0981, 0x0982, 686 0x0984, 0x0985, 687 0x098d, 0x098f, 688 0x0991, 0x0993, 689 0x09a9, 0x09aa, 690 0x09b1, 0x09b2, 691 0x09b3, 0x09b6, 692 0x09ba, 0x09bd, 693 0x09c1, 0x09c7, 694 0x09c9, 0x09cb, 695 0x09cd, 0x09ce, 696 0x09cf, 0x09d7, 697 0x09d8, 0x09dc, 698 0x09de, 0x09df, 699 0x09e2, 0x09e6, 700 0x09f2, 0x09f4, 701 0x09fb, 0x09fc, 702 0x09fe, 0x0a03, 703 0x0a04, 0x0a05, 704 0x0a0b, 0x0a0f, 705 0x0a11, 0x0a13, 706 0x0a29, 0x0a2a, 707 0x0a31, 0x0a32, 708 0x0a34, 0x0a35, 709 0x0a37, 0x0a38, 710 0x0a3a, 0x0a3e, 711 0x0a41, 0x0a59, 712 0x0a5d, 0x0a5e, 713 0x0a5f, 0x0a66, 714 0x0a70, 0x0a72, 715 0x0a75, 0x0a76, 716 0x0a73, 0x0a83, 717 0x0a84, 0x0a85, 718 0x0a8e, 0x0a8f, 719 0x0a92, 0x0a93, 720 0x0aa9, 0x0aaa, 721 0x0ab1, 0x0ab2, 722 0x0ab4, 0x0ab5, 723 0x0aba, 0x0abd, 724 0x0ac1, 0x0ac9, 725 0x0aca, 0x0acb, 726 0x0acd, 0x0ad0, 727 0x0ad1, 0x0ae0, 728 0x0ae2, 0x0ae6, 729 0x0af1, 0x0af9, 730 0x0afa, 0x0b02, 731 0x0b04, 0x0b05, 732 0x0b0d, 0x0b0f, 733 0x0b11, 0x0b13, 734 0x0b29, 0x0b2a, 735 0x0b31, 0x0b32, 736 0x0b34, 0x0b35, 737 0x0b3a, 0x0b3d, 738 0x0b3f, 0x0b40, 739 0x0b41, 0x0b47, 740 0x0b49, 0x0b4b, 741 0x0b4d, 0x0b57, 742 0x0b58, 0x0b5c, 743 0x0b5e, 0x0b5f, 744 0x0b62, 0x0b66, 745 0x0b78, 0x0b83, 746 0x0b84, 0x0b85, 747 0x0b8b, 0x0b8e, 748 0x0b91, 0x0b92, 749 0x0b96, 0x0b99, 750 0x0b9b, 0x0b9c, 751 0x0b9d, 0x0b9e, 752 0x0ba0, 0x0ba3, 753 0x0ba5, 0x0ba8, 754 0x0bab, 0x0bae, 755 0x0bba, 0x0bbe, 756 0x0bc0, 0x0bc1, 757 0x0bc3, 0x0bc6, 758 0x0bc9, 0x0bca, 759 0x0bcd, 0x0bd0, 760 0x0bd1, 0x0bd7, 761 0x0bd8, 0x0be6, 762 0x0bf3, 0x0c01, 763 0x0c04, 0x0c05, 764 0x0c0d, 0x0c0e, 765 0x0c11, 0x0c12, 766 0x0c29, 0x0c2a, 767 0x0c3a, 0x0c3d, 768 0x0c3e, 0x0c41, 769 0x0c45, 0x0c58, 770 0x0c5b, 0x0c60, 771 0x0c62, 0x0c66, 772 0x0c70, 0x0c7f, 773 0x0c81, 0x0c82, 774 0x0c8d, 0x0c8e, 775 0x0c91, 0x0c92, 776 0x0ca9, 0x0caa, 777 0x0cb4, 0x0cb5, 778 0x0cba, 0x0cbd, 779 0x0cc5, 0x0cc6, 780 0x0cc9, 0x0cca, 781 0x0ccc, 0x0cd5, 782 0x0cd7, 0x0cde, 783 0x0cdf, 0x0ce0, 784 0x0ce2, 0x0ce6, 785 0x0cf0, 0x0cf1, 786 0x0cf3, 0x0d02, 787 0x0d04, 0x0d05, 788 0x0d0d, 0x0d0e, 789 0x0d11, 0x0d12, 790 0x0d3b, 0x0d3d, 791 0x0d41, 0x0d46, 792 0x0d49, 0x0d4a, 793 0x0d4d, 0x0d4e, 794 0x0d62, 0x0d66, 795 0x0d80, 0x0d82, 796 0x0d84, 0x0d85, 797 0x0d97, 0x0d9a, 798 0x0db2, 0x0db3, 799 0x0dbc, 0x0dbd, 800 0x0dbe, 0x0dc0, 801 0x0dc7, 0x0dcf, 802 0x0dd2, 0x0dd8, 803 0x0de0, 0x0de6, 804 0x0df0, 0x0df2, 805 0x0df5, 0x0e01, 806 0x0e31, 0x0e32, 807 0x0e34, 0x0e40, 808 0x0e47, 0x0e4f, 809 0x0e5c, 0x0e81, 810 0x0e83, 0x0e84, 811 0x0e85, 0x0e87, 812 0x0e89, 0x0e8a, 813 0x0e8b, 0x0e8d, 814 0x0e8e, 0x0e94, 815 0x0e98, 0x0e99, 816 0x0ea0, 0x0ea1, 817 0x0ea4, 0x0ea5, 818 0x0ea6, 0x0ea7, 819 0x0ea8, 0x0eaa, 820 0x0eac, 0x0ead, 821 0x0eb1, 0x0eb2, 822 0x0eb4, 0x0ebd, 823 0x0ebe, 0x0ec0, 824 0x0ec5, 0x0ec6, 825 0x0ec7, 0x0ed0, 826 0x0eda, 0x0edc, 827 0x0ee0, 0x0f00, 828 0x0f18, 0x0f1a, 829 0x0f35, 0x0f36, 830 0x0f37, 0x0f38, 831 0x0f39, 0x0f3e, 832 0x0f48, 0x0f49, 833 0x0f6d, 0x0f7f, 834 0x0f80, 0x0f85, 835 0x0f86, 0x0f88, 836 0x0f8d, 0x0fbe, 837 0x0fc6, 0x0fc7, 838 0x0fcd, 0x0fce, 839 0x0fdb, 0x1000, 840 0x102d, 0x1031, 841 0x1032, 0x1038, 842 0x1039, 0x103b, 843 0x103d, 0x103f, 844 0x1058, 0x105a, 845 0x105e, 0x1061, 846 0x1071, 0x1075, 847 0x1082, 0x1083, 848 0x1085, 0x1087, 849 0x108d, 0x108e, 850 0x109d, 0x109e, 851 0x10c6, 0x10c7, 852 0x10c8, 0x10cd, 853 0x10ce, 0x10d0, 854 0x1249, 0x124a, 855 0x124e, 0x1250, 856 0x1257, 0x1258, 857 0x1259, 0x125a, 858 0x125e, 0x1260, 859 0x1289, 0x128a, 860 0x128e, 0x1290, 861 0x12b1, 0x12b2, 862 0x12b6, 0x12b8, 863 0x12bf, 0x12c0, 864 0x12c1, 0x12c2, 865 0x12c6, 0x12c8, 866 0x12d7, 0x12d8, 867 0x1311, 0x1312, 868 0x1316, 0x1318, 869 0x135b, 0x1360, 870 0x137d, 0x1380, 871 0x1390, 0x13a0, 872 0x13f6, 0x13f8, 873 0x13fe, 0x1401, 874 0x1680, 0x1681, 875 0x169b, 0x16a0, 876 0x16f9, 0x1700, 877 0x170d, 0x170e, 878 0x1712, 0x1720, 879 0x1732, 0x1735, 880 0x1737, 0x1740, 881 0x1752, 0x1760, 882 0x176d, 0x176e, 883 0x1771, 0x1780, 884 0x17b4, 0x17b6, 885 0x17b7, 0x17be, 886 0x17c6, 0x17c7, 887 0x17c9, 0x17d4, 888 0x17db, 0x17dc, 889 0x17dd, 0x17e0, 890 0x17ea, 0x1810, 891 0x181a, 0x1820, 892 0x1879, 0x1884, 893 0x1885, 0x1887, 894 0x18a9, 0x18aa, 895 0x18ab, 0x18b0, 896 0x18f6, 0x1900, 897 0x191f, 0x1923, 898 0x1927, 0x1929, 899 0x192c, 0x1930, 900 0x1932, 0x1933, 901 0x1939, 0x1946, 902 0x196e, 0x1970, 903 0x1975, 0x1980, 904 0x19ac, 0x19b0, 905 0x19ca, 0x19d0, 906 0x19db, 0x1a00, 907 0x1a17, 0x1a19, 908 0x1a1b, 0x1a1e, 909 0x1a56, 0x1a57, 910 0x1a58, 0x1a61, 911 0x1a62, 0x1a63, 912 0x1a65, 0x1a6d, 913 0x1a73, 0x1a80, 914 0x1a8a, 0x1a90, 915 0x1a9a, 0x1aa0, 916 0x1aae, 0x1b04, 917 0x1b34, 0x1b35, 918 0x1b36, 0x1b3b, 919 0x1b3c, 0x1b3d, 920 0x1b42, 0x1b43, 921 0x1b4c, 0x1b50, 922 0x1b6b, 0x1b74, 923 0x1b7d, 0x1b82, 924 0x1ba2, 0x1ba6, 925 0x1ba8, 0x1baa, 926 0x1bab, 0x1bae, 927 0x1be6, 0x1be7, 928 0x1be8, 0x1bea, 929 0x1bed, 0x1bee, 930 0x1bef, 0x1bf2, 931 0x1bf4, 0x1bfc, 932 0x1c2c, 0x1c34, 933 0x1c36, 0x1c3b, 934 0x1c4a, 0x1c4d, 935 0x1c89, 0x1c90, 936 0x1cbb, 0x1cbd, 937 0x1cc8, 0x1cd3, 938 0x1cd4, 0x1ce1, 939 0x1ce2, 0x1ce9, 940 0x1ced, 0x1cee, 941 0x1cf4, 0x1cf5, 942 0x1cf8, 0x1d00, 943 0x1dc0, 0x1e00, 944 0x1f16, 0x1f18, 945 0x1f1e, 0x1f20, 946 0x1f46, 0x1f48, 947 0x1f4e, 0x1f50, 948 0x1f58, 0x1f59, 949 0x1f5a, 0x1f5b, 950 0x1f5c, 0x1f5d, 951 0x1f5e, 0x1f5f, 952 0x1f7e, 0x1f80, 953 0x1fb5, 0x1fb6, 954 0x1fbd, 0x1fbe, 955 0x1fbf, 0x1fc2, 956 0x1fc5, 0x1fc6, 957 0x1fcd, 0x1fd0, 958 0x1fd4, 0x1fd6, 959 0x1fdc, 0x1fe0, 960 0x1fed, 0x1ff2, 961 0x1ff5, 0x1ff6, 962 0x1ffd, 0x200e, 963 0x2010, 0x2071, 964 0x2072, 0x207f, 965 0x2080, 0x2090, 966 0x209d, 0x2102, 967 0x2103, 0x2107, 968 0x2108, 0x210a, 969 0x2114, 0x2115, 970 0x2116, 0x2119, 971 0x211e, 0x2124, 972 0x2125, 0x2126, 973 0x2127, 0x2128, 974 0x2129, 0x212a, 975 0x212e, 0x212f, 976 0x213a, 0x213c, 977 0x2140, 0x2145, 978 0x214a, 0x214e, 979 0x2150, 0x2160, 980 0x2189, 0x2336, 981 0x237b, 0x2395, 982 0x2396, 0x249c, 983 0x24ea, 0x26ac, 984 0x26ad, 0x2800, 985 0x2900, 0x2c00, 986 0x2c2f, 0x2c30, 987 0x2c5f, 0x2c60, 988 0x2ce5, 0x2ceb, 989 0x2cef, 0x2cf2, 990 0x2cf4, 0x2d00, 991 0x2d26, 0x2d27, 992 0x2d28, 0x2d2d, 993 0x2d2e, 0x2d30, 994 0x2d68, 0x2d6f, 995 0x2d71, 0x2d80, 996 0x2d97, 0x2da0, 997 0x2da7, 0x2da8, 998 0x2daf, 0x2db0, 999 0x2db7, 0x2db8, 1000 0x2dbf, 0x2dc0, 1001 0x2dc7, 0x2dc8, 1002 0x2dcf, 0x2dd0, 1003 0x2dd7, 0x2dd8, 1004 0x2ddf, 0x3005, 1005 0x3008, 0x3021, 1006 0x302a, 0x302e, 1007 0x3030, 0x3031, 1008 0x3036, 0x3038, 1009 0x303d, 0x3041, 1010 0x3097, 0x309d, 1011 0x30a0, 0x30a1, 1012 0x30fb, 0x30fc, 1013 0x3100, 0x3105, 1014 0x3130, 0x3131, 1015 0x318f, 0x3190, 1016 0x31bb, 0x31f0, 1017 0x321d, 0x3220, 1018 0x3250, 0x3260, 1019 0x327c, 0x327f, 1020 0x32b1, 0x32c0, 1021 0x32cc, 0x32d0, 1022 0x32ff, 0x3300, 1023 0x3377, 0x337b, 1024 0x33de, 0x33e0, 1025 0x33ff, 0x3400, 1026 0x4db6, 0x4e00, 1027 0x9ff0, 0xa000, 1028 0xa48d, 0xa4d0, 1029 0xa60d, 0xa610, 1030 0xa62c, 0xa640, 1031 0xa66f, 0xa680, 1032 0xa69e, 0xa6a0, 1033 0xa6f0, 0xa6f2, 1034 0xa6f8, 0xa722, 1035 0xa788, 0xa789, 1036 0xa7ba, 0xa7f7, 1037 0xa802, 0xa803, 1038 0xa806, 0xa807, 1039 0xa80b, 0xa80c, 1040 0xa825, 0xa827, 1041 0xa828, 0xa830, 1042 0xa838, 0xa840, 1043 0xa874, 0xa880, 1044 0xa8c4, 0xa8ce, 1045 0xa8da, 0xa8f2, 1046 0xa8ff, 0xa900, 1047 0xa926, 0xa92e, 1048 0xa947, 0xa952, 1049 0xa954, 0xa95f, 1050 0xa97d, 0xa983, 1051 0xa9b3, 0xa9b4, 1052 0xa9b6, 0xa9ba, 1053 0xa9bc, 0xa9bd, 1054 0xa9ce, 0xa9cf, 1055 0xa9da, 0xa9de, 1056 0xa9e5, 0xa9e6, 1057 0xa9ff, 0xaa00, 1058 0xaa29, 0xaa2f, 1059 0xaa31, 0xaa33, 1060 0xaa35, 0xaa40, 1061 0xaa43, 0xaa44, 1062 0xaa4c, 0xaa4d, 1063 0xaa4e, 0xaa50, 1064 0xaa5a, 0xaa5c, 1065 0xaa7c, 0xaa7d, 1066 0xaab0, 0xaab1, 1067 0xaab2, 0xaab5, 1068 0xaab7, 0xaab9, 1069 0xaabe, 0xaac0, 1070 0xaac1, 0xaac2, 1071 0xaac3, 0xaadb, 1072 0xaaec, 0xaaee, 1073 0xaaf6, 0xab01, 1074 0xab07, 0xab09, 1075 0xab0f, 0xab11, 1076 0xab17, 0xab20, 1077 0xab27, 0xab28, 1078 0xab2f, 0xab30, 1079 0xab66, 0xab70, 1080 0xabe5, 0xabe6, 1081 0xabe8, 0xabe9, 1082 0xabed, 0xabf0, 1083 0xabfa, 0xac00, 1084 0xd7a4, 0xd7b0, 1085 0xd7c7, 0xd7cb, 1086 0xd7fc, 0xe000, 1087 0xfa6e, 0xfa70, 1088 0xfada, 0xfb00, 1089 0xfb07, 0xfb13, 1090 0xfb18, 0xfb1d, 1091 0xfb1e, 0xfb1f, 1092 0xfb29, 0xfb2a, 1093 0xfd3e, 0xfd40, 1094 0xfdd0, 0xfdf0, 1095 0xfdfd, 0xfdfe, 1096 0xfe00, 0xfe70, 1097 0xfeff, 0xff21, 1098 0xff3b, 0xff41, 1099 0xff5b, 0xff66, 1100 0xffbf, 0xffc2, 1101 0xffc8, 0xffca, 1102 0xffd0, 0xffd2, 1103 0xffd8, 0xffda, 1104 0xffdd, 0x10000, 1105 0x1000c, 0x1000d, 1106 0x10027, 0x10028, 1107 0x1003b, 0x1003c, 1108 0x1003e, 0x1003f, 1109 0x1004e, 0x10050, 1110 0x1005e, 0x10080, 1111 0x100fb, 0x10100, 1112 0x10101, 0x10102, 1113 0x10103, 0x10107, 1114 0x10134, 0x10137, 1115 0x10140, 0x1018d, 1116 0x1018f, 0x101d0, 1117 0x101fd, 0x10280, 1118 0x1029d, 0x102a0, 1119 0x102d1, 0x10300, 1120 0x10324, 0x1032d, 1121 0x1034b, 0x10350, 1122 0x10376, 0x10380, 1123 0x1039e, 0x1039f, 1124 0x103c4, 0x103c8, 1125 0x103d6, 0x10400, 1126 0x1049e, 0x104a0, 1127 0x104aa, 0x104d3, 1128 0x104d4, 0x104d8, 1129 0x104fc, 0x10500, 1130 0x10528, 0x10530, 1131 0x10564, 0x1056f, 1132 0x10570, 0x10600, 1133 0x10737, 0x10740, 1134 0x10756, 0x10760, 1135 0x10768, 0x10800, 1136 0x1091f, 0x10920, 1137 0x10a01, 0x10a04, 1138 0x10a05, 0x10a07, 1139 0x10a0c, 0x10a10, 1140 0x10a38, 0x10a3b, 1141 0x10a3f, 0x10a40, 1142 0x10ae5, 0x10ae7, 1143 0x10b39, 0x10b40, 1144 0x10d00, 0x10d40, 1145 0x10e60, 0x10e7f, 1146 0x10f30, 0x10f70, 1147 0x11001, 0x11002, 1148 0x11038, 0x11047, 1149 0x1104e, 0x11066, 1150 0x11070, 0x11082, 1151 0x110b3, 0x110b7, 1152 0x110b9, 0x110bb, 1153 0x110c2, 0x110cd, 1154 0x110ce, 0x110d0, 1155 0x110e9, 0x110f0, 1156 0x110fa, 0x11103, 1157 0x11127, 0x1112c, 1158 0x1112d, 0x11136, 1159 0x11147, 0x11150, 1160 0x11173, 0x11174, 1161 0x11177, 0x11182, 1162 0x111b6, 0x111bf, 1163 0x111c9, 0x111cd, 1164 0x111ce, 0x111d0, 1165 0x111e0, 0x111e1, 1166 0x111f5, 0x11200, 1167 0x11212, 0x11213, 1168 0x1122f, 0x11232, 1169 0x11234, 0x11235, 1170 0x11236, 0x11238, 1171 0x1123e, 0x11280, 1172 0x11287, 0x11288, 1173 0x11289, 0x1128a, 1174 0x1128e, 0x1128f, 1175 0x1129e, 0x1129f, 1176 0x112aa, 0x112b0, 1177 0x112df, 0x112e0, 1178 0x112e3, 0x112f0, 1179 0x112fa, 0x11302, 1180 0x11304, 0x11305, 1181 0x1130d, 0x1130f, 1182 0x11311, 0x11313, 1183 0x11329, 0x1132a, 1184 0x11331, 0x11332, 1185 0x11334, 0x11335, 1186 0x1133a, 0x1133d, 1187 0x11340, 0x11341, 1188 0x11345, 0x11347, 1189 0x11349, 0x1134b, 1190 0x1134e, 0x11350, 1191 0x11351, 0x11357, 1192 0x11358, 0x1135d, 1193 0x11364, 0x11400, 1194 0x11438, 0x11440, 1195 0x11442, 0x11445, 1196 0x11446, 0x11447, 1197 0x1145a, 0x1145b, 1198 0x1145c, 0x1145d, 1199 0x1145e, 0x11480, 1200 0x114b3, 0x114b9, 1201 0x114ba, 0x114bb, 1202 0x114bf, 0x114c1, 1203 0x114c2, 0x114c4, 1204 0x114c8, 0x114d0, 1205 0x114da, 0x11580, 1206 0x115b2, 0x115b8, 1207 0x115bc, 0x115be, 1208 0x115bf, 0x115c1, 1209 0x115dc, 0x11600, 1210 0x11633, 0x1163b, 1211 0x1163d, 0x1163e, 1212 0x1163f, 0x11641, 1213 0x11645, 0x11650, 1214 0x1165a, 0x11680, 1215 0x116ab, 0x116ac, 1216 0x116ad, 0x116ae, 1217 0x116b0, 0x116b6, 1218 0x116b7, 0x116c0, 1219 0x116ca, 0x11700, 1220 0x1171b, 0x11720, 1221 0x11722, 0x11726, 1222 0x11727, 0x11730, 1223 0x1182f, 0x11838, 1224 0x11839, 0x1183b, 1225 0x1183c, 0x118a0, 1226 0x118f3, 0x118ff, 1227 0x11900, 0x11a00, 1228 0x11a01, 0x11a07, 1229 0x11a09, 0x11a0b, 1230 0x11a33, 0x11a3a, 1231 0x11a3b, 0x11a3f, 1232 0x11a47, 0x11a50, 1233 0x11a51, 0x11a57, 1234 0x11a59, 0x11a5c, 1235 0x11a84, 0x11a86, 1236 0x11a8a, 0x11a97, 1237 0x11a98, 0x11a9a, 1238 0x11aa3, 0x11ac0, 1239 0x11af9, 0x11c00, 1240 0x11c09, 0x11c0a, 1241 0x11c30, 0x11c3e, 1242 0x11c46, 0x11c50, 1243 0x11c6d, 0x11c70, 1244 0x11c90, 0x11ca9, 1245 0x11caa, 0x11cb1, 1246 0x11cb2, 0x11cb4, 1247 0x11cb5, 0x11d00, 1248 0x11d07, 0x11d08, 1249 0x11d0a, 0x11d0b, 1250 0x11d31, 0x11d46, 1251 0x11d47, 0x11d50, 1252 0x11d5a, 0x11d60, 1253 0x11d66, 0x11d67, 1254 0x11d69, 0x11d6a, 1255 0x11d8f, 0x11d93, 1256 0x11d95, 0x11d96, 1257 0x11d97, 0x11d98, 1258 0x11d99, 0x11da0, 1259 0x11daa, 0x11ee0, 1260 0x11ef3, 0x11ef5, 1261 0x11ef9, 0x12000, 1262 0x1239a, 0x12400, 1263 0x1246f, 0x12470, 1264 0x12475, 0x12480, 1265 0x12544, 0x13000, 1266 0x1342f, 0x14400, 1267 0x14647, 0x16800, 1268 0x16a39, 0x16a40, 1269 0x16a5f, 0x16a60, 1270 0x16a6a, 0x16a6e, 1271 0x16a70, 0x16ad0, 1272 0x16aee, 0x16af5, 1273 0x16af6, 0x16b00, 1274 0x16b30, 0x16b37, 1275 0x16b46, 0x16b50, 1276 0x16b5a, 0x16b5b, 1277 0x16b62, 0x16b63, 1278 0x16b78, 0x16b7d, 1279 0x16b90, 0x16e40, 1280 0x16e9b, 0x16f00, 1281 0x16f45, 0x16f50, 1282 0x16f7f, 0x16f93, 1283 0x16fa0, 0x16fe0, 1284 0x16fe2, 0x17000, 1285 0x187f2, 0x18800, 1286 0x18af3, 0x1b000, 1287 0x1b11f, 0x1b170, 1288 0x1b2fc, 0x1bc00, 1289 0x1bc6b, 0x1bc70, 1290 0x1bc7d, 0x1bc80, 1291 0x1bc89, 0x1bc90, 1292 0x1bc9a, 0x1bc9c, 1293 0x1bc9d, 0x1bc9f, 1294 0x1bca0, 0x1d000, 1295 0x1d0f6, 0x1d100, 1296 0x1d127, 0x1d129, 1297 0x1d167, 0x1d16a, 1298 0x1d173, 0x1d183, 1299 0x1d185, 0x1d18c, 1300 0x1d1aa, 0x1d1ae, 1301 0x1d1e9, 0x1d2e0, 1302 0x1d2f4, 0x1d360, 1303 0x1d379, 0x1d400, 1304 0x1d455, 0x1d456, 1305 0x1d49d, 0x1d49e, 1306 0x1d4a0, 0x1d4a2, 1307 0x1d4a3, 0x1d4a5, 1308 0x1d4a7, 0x1d4a9, 1309 0x1d4ad, 0x1d4ae, 1310 0x1d4ba, 0x1d4bb, 1311 0x1d4bc, 0x1d4bd, 1312 0x1d4c4, 0x1d4c5, 1313 0x1d506, 0x1d507, 1314 0x1d50b, 0x1d50d, 1315 0x1d515, 0x1d516, 1316 0x1d51d, 0x1d51e, 1317 0x1d53a, 0x1d53b, 1318 0x1d53f, 0x1d540, 1319 0x1d545, 0x1d546, 1320 0x1d547, 0x1d54a, 1321 0x1d551, 0x1d552, 1322 0x1d6a6, 0x1d6a8, 1323 0x1d6db, 0x1d6dc, 1324 0x1d715, 0x1d716, 1325 0x1d74f, 0x1d750, 1326 0x1d789, 0x1d78a, 1327 0x1d7c3, 0x1d7c4, 1328 0x1d7cc, 0x1d800, 1329 0x1da00, 0x1da37, 1330 0x1da3b, 0x1da6d, 1331 0x1da75, 0x1da76, 1332 0x1da84, 0x1da85, 1333 0x1da8c, 0x1e800, 1334 0x1e8d0, 0x1e8d7, 1335 0x1e944, 0x1e94b, 1336 0x1ec70, 0x1ecc0, 1337 0x1ee00, 0x1ef00, 1338 0x1f000, 0x1f110, 1339 0x1f12f, 0x1f130, 1340 0x1f16a, 0x1f170, 1341 0x1f1ad, 0x1f1e6, 1342 0x1f203, 0x1f210, 1343 0x1f23c, 0x1f240, 1344 0x1f249, 0x1f250, 1345 0x1f252, 0x20000, 1346 0x2a6d7, 0x2a700, 1347 0x2b735, 0x2b740, 1348 0x2b81e, 0x2b820, 1349 0x2cea2, 0x2ceb0, 1350 0x2ebe1, 0x2f800, 1351 0x2fa1e, 0xf0000, 1352 0xffffe, 0x100000, 1353 0x10fffe, 0x10ffff // sentinel 1354 }; 1355 1356 1357 // use a binary search with a cache 1358 1359 private transient volatile int stCache = 0; 1360 1361 private boolean isStrongDirectional(char c) { 1362 int cachedIndex = stCache; 1363 if (c < strongTable[cachedIndex]) { 1364 cachedIndex = search(c, strongTable, 0, cachedIndex); 1365 } else if (c >= strongTable[cachedIndex + 1]) { 1366 cachedIndex = search(c, strongTable, cachedIndex + 1, 1367 strongTable.length - cachedIndex - 1); 1368 } 1369 boolean val = (cachedIndex & 0x1) == 1; 1370 stCache = cachedIndex; 1371 return val; 1372 } 1373 1374 private static int getKeyFromMask(int mask) { 1375 int key = 0; 1376 while (key < NUM_KEYS && ((mask & (1<<key)) == 0)) { 1377 ++key; 1378 } 1379 if (key == NUM_KEYS || ((mask & ~(1<<key)) != 0)) { 1380 throw new IllegalArgumentException("invalid shaper: " + Integer.toHexString(mask)); 1381 } 1382 return key; 1383 } 1384 1385 /** 1386 * Returns a shaper for the provided unicode range. All 1387 * Latin-1 (EUROPEAN) digits are converted 1388 * to the corresponding decimal unicode digits. 1389 * @param singleRange the specified Unicode range 1390 * @return a non-contextual numeric shaper 1391 * @throws IllegalArgumentException if the range is not a single range 1392 */ 1393 public static NumericShaper getShaper(int singleRange) { 1394 int key = getKeyFromMask(singleRange); 1395 return new NumericShaper(key, singleRange); 1396 } 1397 1398 /** 1399 * Returns a shaper for the provided Unicode 1400 * range. All Latin-1 (EUROPEAN) digits are converted to the 1401 * corresponding decimal digits of the specified Unicode range. 1402 * 1403 * @param singleRange the Unicode range given by a {@link 1404 * NumericShaper.Range} constant. 1405 * @return a non-contextual {@code NumericShaper}. 1406 * @throws NullPointerException if {@code singleRange} is {@code null} 1407 * @since 1.7 1408 */ 1409 public static NumericShaper getShaper(Range singleRange) { 1410 return new NumericShaper(singleRange, EnumSet.of(singleRange)); 1411 } 1412 1413 /** 1414 * Returns a contextual shaper for the provided unicode range(s). 1415 * Latin-1 (EUROPEAN) digits are converted to the decimal digits 1416 * corresponding to the range of the preceding text, if the 1417 * range is one of the provided ranges. Multiple ranges are 1418 * represented by or-ing the values together, such as, 1419 * {@code NumericShaper.ARABIC | NumericShaper.THAI}. The 1420 * shaper assumes EUROPEAN as the starting context, that is, if 1421 * EUROPEAN digits are encountered before any strong directional 1422 * text in the string, the context is presumed to be EUROPEAN, and 1423 * so the digits will not shape. 1424 * @param ranges the specified Unicode ranges 1425 * @return a shaper for the specified ranges 1426 */ 1427 public static NumericShaper getContextualShaper(int ranges) { 1428 ranges |= CONTEXTUAL_MASK; 1429 return new NumericShaper(EUROPEAN_KEY, ranges); 1430 } 1431 1432 /** 1433 * Returns a contextual shaper for the provided Unicode 1434 * range(s). The Latin-1 (EUROPEAN) digits are converted to the 1435 * decimal digits corresponding to the range of the preceding 1436 * text, if the range is one of the provided ranges. 1437 * 1438 * <p>The shaper assumes EUROPEAN as the starting context, that 1439 * is, if EUROPEAN digits are encountered before any strong 1440 * directional text in the string, the context is presumed to be 1441 * EUROPEAN, and so the digits will not shape. 1442 * 1443 * @param ranges the specified Unicode ranges 1444 * @return a contextual shaper for the specified ranges 1445 * @throws NullPointerException if {@code ranges} is {@code null}. 1446 * @since 1.7 1447 */ 1448 public static NumericShaper getContextualShaper(Set<Range> ranges) { 1449 NumericShaper shaper = new NumericShaper(Range.EUROPEAN, ranges); 1450 shaper.mask = CONTEXTUAL_MASK; 1451 return shaper; 1452 } 1453 1454 /** 1455 * Returns a contextual shaper for the provided unicode range(s). 1456 * Latin-1 (EUROPEAN) digits will be converted to the decimal digits 1457 * corresponding to the range of the preceding text, if the 1458 * range is one of the provided ranges. Multiple ranges are 1459 * represented by or-ing the values together, for example, 1460 * {@code NumericShaper.ARABIC | NumericShaper.THAI}. The 1461 * shaper uses defaultContext as the starting context. 1462 * @param ranges the specified Unicode ranges 1463 * @param defaultContext the starting context, such as 1464 * {@code NumericShaper.EUROPEAN} 1465 * @return a shaper for the specified Unicode ranges. 1466 * @throws IllegalArgumentException if the specified 1467 * {@code defaultContext} is not a single valid range. 1468 */ 1469 public static NumericShaper getContextualShaper(int ranges, int defaultContext) { 1470 int key = getKeyFromMask(defaultContext); 1471 ranges |= CONTEXTUAL_MASK; 1472 return new NumericShaper(key, ranges); 1473 } 1474 1475 /** 1476 * Returns a contextual shaper for the provided Unicode range(s). 1477 * The Latin-1 (EUROPEAN) digits will be converted to the decimal 1478 * digits corresponding to the range of the preceding text, if the 1479 * range is one of the provided ranges. The shaper uses {@code 1480 * defaultContext} as the starting context. 1481 * 1482 * @param ranges the specified Unicode ranges 1483 * @param defaultContext the starting context, such as 1484 * {@code NumericShaper.Range.EUROPEAN} 1485 * @return a contextual shaper for the specified Unicode ranges. 1486 * @throws NullPointerException 1487 * if {@code ranges} or {@code defaultContext} is {@code null} 1488 * @since 1.7 1489 */ 1490 public static NumericShaper getContextualShaper(Set<Range> ranges, 1491 Range defaultContext) { 1492 if (defaultContext == null) { 1493 throw new NullPointerException(); 1494 } 1495 NumericShaper shaper = new NumericShaper(defaultContext, ranges); 1496 shaper.mask = CONTEXTUAL_MASK; 1497 return shaper; 1498 } 1499 1500 /** 1501 * Private constructor. 1502 */ 1503 private NumericShaper(int key, int mask) { 1504 this.key = key; 1505 this.mask = mask; 1506 } 1507 1508 private NumericShaper(Range defaultContext, Set<Range> ranges) { 1509 shapingRange = defaultContext; 1510 rangeSet = EnumSet.copyOf(ranges); // throws NPE if ranges is null. 1511 1512 // Give precedence to EASTERN_ARABIC if both ARABIC and 1513 // EASTERN_ARABIC are specified. 1514 if (rangeSet.contains(Range.EASTERN_ARABIC) 1515 && rangeSet.contains(Range.ARABIC)) { 1516 rangeSet.remove(Range.ARABIC); 1517 } 1518 1519 // As well as the above case, give precedence to TAI_THAM_THAM if both 1520 // TAI_THAM_HORA and TAI_THAM_THAM are specified. 1521 if (rangeSet.contains(Range.TAI_THAM_THAM) 1522 && rangeSet.contains(Range.TAI_THAM_HORA)) { 1523 rangeSet.remove(Range.TAI_THAM_HORA); 1524 } 1525 1526 rangeArray = rangeSet.toArray(new Range[rangeSet.size()]); 1527 if (rangeArray.length > BSEARCH_THRESHOLD) { 1528 // sort rangeArray for binary search 1529 Arrays.sort(rangeArray, 1530 new Comparator<Range>() { 1531 public int compare(Range s1, Range s2) { 1532 return s1.base > s2.base ? 1 : s1.base == s2.base ? 0 : -1; 1533 } 1534 }); 1535 } 1536 } 1537 1538 /** 1539 * Converts the digits in the text that occur between start and 1540 * start + count. 1541 * @param text an array of characters to convert 1542 * @param start the index into {@code text} to start 1543 * converting 1544 * @param count the number of characters in {@code text} 1545 * to convert 1546 * @throws IndexOutOfBoundsException if start or start + count is 1547 * out of bounds 1548 * @throws NullPointerException if text is null 1549 */ 1550 public void shape(char[] text, int start, int count) { 1551 checkParams(text, start, count); 1552 if (isContextual()) { 1553 if (rangeSet == null) { 1554 shapeContextually(text, start, count, key); 1555 } else { 1556 shapeContextually(text, start, count, shapingRange); 1557 } 1558 } else { 1559 shapeNonContextually(text, start, count); 1560 } 1561 } 1562 1563 /** 1564 * Converts the digits in the text that occur between start and 1565 * start + count, using the provided context. 1566 * Context is ignored if the shaper is not a contextual shaper. 1567 * @param text an array of characters 1568 * @param start the index into {@code text} to start 1569 * converting 1570 * @param count the number of characters in {@code text} 1571 * to convert 1572 * @param context the context to which to convert the 1573 * characters, such as {@code NumericShaper.EUROPEAN} 1574 * @throws IndexOutOfBoundsException if start or start + count is 1575 * out of bounds 1576 * @throws NullPointerException if text is null 1577 * @throws IllegalArgumentException if this is a contextual shaper 1578 * and the specified {@code context} is not a single valid 1579 * range. 1580 */ 1581 public void shape(char[] text, int start, int count, int context) { 1582 checkParams(text, start, count); 1583 if (isContextual()) { 1584 int ctxKey = getKeyFromMask(context); 1585 if (rangeSet == null) { 1586 shapeContextually(text, start, count, ctxKey); 1587 } else { 1588 shapeContextually(text, start, count, Range.values()[ctxKey]); 1589 } 1590 } else { 1591 shapeNonContextually(text, start, count); 1592 } 1593 } 1594 1595 /** 1596 * Converts the digits in the text that occur between {@code 1597 * start} and {@code start + count}, using the provided {@code 1598 * context}. {@code Context} is ignored if the shaper is not a 1599 * contextual shaper. 1600 * 1601 * @param text a {@code char} array 1602 * @param start the index into {@code text} to start converting 1603 * @param count the number of {@code char}s in {@code text} 1604 * to convert 1605 * @param context the context to which to convert the characters, 1606 * such as {@code NumericShaper.Range.EUROPEAN} 1607 * @throws IndexOutOfBoundsException 1608 * if {@code start} or {@code start + count} is out of bounds 1609 * @throws NullPointerException 1610 * if {@code text} or {@code context} is null 1611 * @since 1.7 1612 */ 1613 public void shape(char[] text, int start, int count, Range context) { 1614 checkParams(text, start, count); 1615 if (context == null) { 1616 throw new NullPointerException("context is null"); 1617 } 1618 1619 if (isContextual()) { 1620 if (rangeSet != null) { 1621 shapeContextually(text, start, count, context); 1622 } else { 1623 int key = Range.toRangeIndex(context); 1624 if (key >= 0) { 1625 shapeContextually(text, start, count, key); 1626 } else { 1627 shapeContextually(text, start, count, shapingRange); 1628 } 1629 } 1630 } else { 1631 shapeNonContextually(text, start, count); 1632 } 1633 } 1634 1635 private void checkParams(char[] text, int start, int count) { 1636 if (text == null) { 1637 throw new NullPointerException("text is null"); 1638 } 1639 if ((start < 0) 1640 || (start > text.length) 1641 || ((start + count) < 0) 1642 || ((start + count) > text.length)) { 1643 throw new IndexOutOfBoundsException( 1644 "bad start or count for text of length " + text.length); 1645 } 1646 } 1647 1648 /** 1649 * Returns a {@code boolean} indicating whether or not 1650 * this shaper shapes contextually. 1651 * @return {@code true} if this shaper is contextual; 1652 * {@code false} otherwise. 1653 */ 1654 public boolean isContextual() { 1655 return (mask & CONTEXTUAL_MASK) != 0; 1656 } 1657 1658 /** 1659 * Returns an {@code int} that ORs together the values for 1660 * all the ranges that will be shaped. 1661 * <p> 1662 * For example, to check if a shaper shapes to Arabic, you would use the 1663 * following: 1664 * <blockquote> 1665 * {@code if ((shaper.getRanges() & shaper.ARABIC) != 0) { ... } 1666 * </blockquote> 1667 * 1668 * <p>Note that this method supports only the bit mask-based 1669 * ranges. Call {@link #getRangeSet()} for the enum-based ranges. 1670 * 1671 * @return the values for all the ranges to be shaped. 1672 */ 1673 public int getRanges() { 1674 return mask & ~CONTEXTUAL_MASK; 1675 } 1676 1677 /** 1678 * Returns a {@code Set} representing all the Unicode ranges in 1679 * this {@code NumericShaper} that will be shaped. 1680 * 1681 * @return all the Unicode ranges to be shaped. 1682 * @since 1.7 1683 */ 1684 public Set<Range> getRangeSet() { 1685 if (rangeSet != null) { 1686 return EnumSet.copyOf(rangeSet); 1687 } 1688 return Range.maskToRangeSet(mask); 1689 } 1690 1691 /** 1692 * Perform non-contextual shaping. 1693 */ 1694 private void shapeNonContextually(char[] text, int start, int count) { 1695 int base; 1696 char minDigit = '0'; 1697 if (shapingRange != null) { 1698 base = shapingRange.getDigitBase(); 1699 minDigit += shapingRange.getNumericBase(); 1700 } else { 1701 base = bases[key]; 1702 if (key == ETHIOPIC_KEY) { 1703 minDigit++; // Ethiopic doesn't use decimal zero 1704 } 1705 } 1706 for (int i = start, e = start + count; i < e; ++i) { 1707 char c = text[i]; 1708 if (c >= minDigit && c <= '\u0039') { 1709 text[i] = (char)(c + base); 1710 } 1711 } 1712 } 1713 1714 /** 1715 * Perform contextual shaping. 1716 * Synchronized to protect caches used in getContextKey. 1717 */ 1718 private synchronized void shapeContextually(char[] text, int start, int count, int ctxKey) { 1719 1720 // if we don't support this context, then don't shape 1721 if ((mask & (1<<ctxKey)) == 0) { 1722 ctxKey = EUROPEAN_KEY; 1723 } 1724 int lastkey = ctxKey; 1725 1726 int base = bases[ctxKey]; 1727 char minDigit = ctxKey == ETHIOPIC_KEY ? '1' : '0'; // Ethiopic doesn't use decimal zero 1728 1729 synchronized (NumericShaper.class) { 1730 for (int i = start, e = start + count; i < e; ++i) { 1731 char c = text[i]; 1732 if (c >= minDigit && c <= '\u0039') { 1733 text[i] = (char)(c + base); 1734 } 1735 1736 if (isStrongDirectional(c)) { 1737 int newkey = getContextKey(c); 1738 if (newkey != lastkey) { 1739 lastkey = newkey; 1740 1741 ctxKey = newkey; 1742 if (((mask & EASTERN_ARABIC) != 0) && 1743 (ctxKey == ARABIC_KEY || 1744 ctxKey == EASTERN_ARABIC_KEY)) { 1745 ctxKey = EASTERN_ARABIC_KEY; 1746 } else if (((mask & ARABIC) != 0) && 1747 (ctxKey == ARABIC_KEY || 1748 ctxKey == EASTERN_ARABIC_KEY)) { 1749 ctxKey = ARABIC_KEY; 1750 } else if ((mask & (1<<ctxKey)) == 0) { 1751 ctxKey = EUROPEAN_KEY; 1752 } 1753 1754 base = bases[ctxKey]; 1755 1756 minDigit = ctxKey == ETHIOPIC_KEY ? '1' : '0'; // Ethiopic doesn't use decimal zero 1757 } 1758 } 1759 } 1760 } 1761 } 1762 1763 private void shapeContextually(char[] text, int start, int count, Range ctxKey) { 1764 // if we don't support the specified context, then don't shape. 1765 if (ctxKey == null || !rangeSet.contains(ctxKey)) { 1766 ctxKey = Range.EUROPEAN; 1767 } 1768 1769 Range lastKey = ctxKey; 1770 int base = ctxKey.getDigitBase(); 1771 char minDigit = (char)('0' + ctxKey.getNumericBase()); 1772 final int end = start + count; 1773 for (int i = start; i < end; ++i) { 1774 char c = text[i]; 1775 if (c >= minDigit && c <= '9') { 1776 text[i] = (char)(c + base); 1777 continue; 1778 } 1779 if (isStrongDirectional(c)) { 1780 ctxKey = rangeForCodePoint(c); 1781 if (ctxKey != lastKey) { 1782 lastKey = ctxKey; 1783 base = ctxKey.getDigitBase(); 1784 minDigit = (char)('0' + ctxKey.getNumericBase()); 1785 } 1786 } 1787 } 1788 } 1789 1790 /** 1791 * Returns a hash code for this shaper. 1792 * @return this shaper's hash code. 1793 * @see java.lang.Object#hashCode 1794 */ 1795 public int hashCode() { 1796 int hash = mask; 1797 if (rangeSet != null) { 1798 // Use the CONTEXTUAL_MASK bit only for the enum-based 1799 // NumericShaper. A deserialized NumericShaper might have 1800 // bit masks. 1801 hash &= CONTEXTUAL_MASK; 1802 hash ^= rangeSet.hashCode(); 1803 } 1804 return hash; 1805 } 1806 1807 /** 1808 * Returns {@code true} if the specified object is an instance of 1809 * {@code NumericShaper} and shapes identically to this one, 1810 * regardless of the range representations, the bit mask or the 1811 * enum. For example, the following code produces {@code "true"}. 1812 * <blockquote><pre> 1813 * NumericShaper ns1 = NumericShaper.getShaper(NumericShaper.ARABIC); 1814 * NumericShaper ns2 = NumericShaper.getShaper(NumericShaper.Range.ARABIC); 1815 * System.out.println(ns1.equals(ns2)); 1816 * </pre></blockquote> 1817 * 1818 * @param o the specified object to compare to this 1819 * {@code NumericShaper} 1820 * @return {@code true} if {@code o} is an instance 1821 * of {@code NumericShaper} and shapes in the same way; 1822 * {@code false} otherwise. 1823 * @see java.lang.Object#equals(java.lang.Object) 1824 */ 1825 public boolean equals(Object o) { 1826 if (o != null) { 1827 try { 1828 NumericShaper rhs = (NumericShaper)o; 1829 if (rangeSet != null) { 1830 if (rhs.rangeSet != null) { 1831 return isContextual() == rhs.isContextual() 1832 && rangeSet.equals(rhs.rangeSet) 1833 && shapingRange == rhs.shapingRange; 1834 } 1835 return isContextual() == rhs.isContextual() 1836 && rangeSet.equals(Range.maskToRangeSet(rhs.mask)) 1837 && shapingRange == Range.indexToRange(rhs.key); 1838 } else if (rhs.rangeSet != null) { 1839 Set<Range> rset = Range.maskToRangeSet(mask); 1840 Range srange = Range.indexToRange(key); 1841 return isContextual() == rhs.isContextual() 1842 && rset.equals(rhs.rangeSet) 1843 && srange == rhs.shapingRange; 1844 } 1845 return rhs.mask == mask && rhs.key == key; 1846 } 1847 catch (ClassCastException e) { 1848 } 1849 } 1850 return false; 1851 } 1852 1853 /** 1854 * Returns a {@code String} that describes this shaper. This method 1855 * is used for debugging purposes only. 1856 * @return a {@code String} describing this shaper. 1857 */ 1858 public String toString() { 1859 StringBuilder buf = new StringBuilder(super.toString()); 1860 1861 buf.append("[contextual:").append(isContextual()); 1862 1863 String[] keyNames = null; 1864 if (isContextual()) { 1865 buf.append(", context:"); 1866 buf.append(shapingRange == null ? Range.values()[key] : shapingRange); 1867 } 1868 1869 if (rangeSet == null) { 1870 buf.append(", range(s): "); 1871 boolean first = true; 1872 for (int i = 0; i < NUM_KEYS; ++i) { 1873 if ((mask & (1 << i)) != 0) { 1874 if (first) { 1875 first = false; 1876 } else { 1877 buf.append(", "); 1878 } 1879 buf.append(Range.values()[i]); 1880 } 1881 } 1882 } else { 1883 buf.append(", range set: ").append(rangeSet); 1884 } 1885 buf.append(']'); 1886 1887 return buf.toString(); 1888 } 1889 1890 /** 1891 * Returns the index of the high bit in value (assuming le, actually 1892 * power of 2 >= value). value must be positive. 1893 */ 1894 private static int getHighBit(int value) { 1895 if (value <= 0) { 1896 return -32; 1897 } 1898 1899 int bit = 0; 1900 1901 if (value >= 1 << 16) { 1902 value >>= 16; 1903 bit += 16; 1904 } 1905 1906 if (value >= 1 << 8) { 1907 value >>= 8; 1908 bit += 8; 1909 } 1910 1911 if (value >= 1 << 4) { 1912 value >>= 4; 1913 bit += 4; 1914 } 1915 1916 if (value >= 1 << 2) { 1917 value >>= 2; 1918 bit += 2; 1919 } 1920 1921 if (value >= 1 << 1) { 1922 bit += 1; 1923 } 1924 1925 return bit; 1926 } 1927 1928 /** 1929 * fast binary search over subrange of array. 1930 */ 1931 private static int search(int value, int[] array, int start, int length) 1932 { 1933 int power = 1 << getHighBit(length); 1934 int extra = length - power; 1935 int probe = power; 1936 int index = start; 1937 1938 if (value >= array[index + extra]) { 1939 index += extra; 1940 } 1941 1942 while (probe > 1) { 1943 probe >>= 1; 1944 1945 if (value >= array[index + probe]) { 1946 index += probe; 1947 } 1948 } 1949 1950 return index; 1951 } 1952 1953 /** 1954 * Converts the {@code NumericShaper.Range} enum-based parameters, 1955 * if any, to the bit mask-based counterparts and writes this 1956 * object to the {@code stream}. Any enum constants that have no 1957 * bit mask-based counterparts are ignored in the conversion. 1958 * 1959 * @param stream the output stream to write to 1960 * @throws IOException if an I/O error occurs while writing to {@code stream} 1961 * @since 1.7 1962 */ 1963 private void writeObject(ObjectOutputStream stream) throws IOException { 1964 if (shapingRange != null) { 1965 int index = Range.toRangeIndex(shapingRange); 1966 if (index >= 0) { 1967 key = index; 1968 } 1969 } 1970 if (rangeSet != null) { 1971 mask |= Range.toRangeMask(rangeSet); 1972 } 1973 stream.defaultWriteObject(); 1974 } 1975 }