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