1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2017 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 package ohos.global.icu.impl.number; 5 6 import ohos.global.icu.impl.FormattedStringBuilder; 7 import ohos.global.icu.text.NumberFormat; 8 import ohos.global.icu.text.UnicodeSet; 9 10 /** 11 * Performs manipulations on affix patterns: the prefix and suffix strings associated with a decimal 12 * format pattern. For example: 13 * 14 * <table> 15 * <tr> 16 * <th>Affix Pattern</th> 17 * <th>Example Unescaped (Formatted) String</th> 18 * </tr> 19 * <tr> 20 * <td>abc</td> 21 * <td>abc</td> 22 * </tr> 23 * <tr> 24 * <td>ab-</td> 25 * <td>ab−</td> 26 * </tr> 27 * <tr> 28 * <td>ab'-'</td> 29 * <td>ab-</td> 30 * </tr> 31 * <tr> 32 * <td>ab''</td> 33 * <td>ab'</td> 34 * </tr> 35 * </table> 36 * 37 * To manually iterate over tokens in a literal string, use the following pattern, which is designed to 38 * be efficient. 39 * 40 * <pre> 41 * long tag = 0L; 42 * while (AffixPatternUtils.hasNext(tag, patternString)) { 43 * tag = AffixPatternUtils.nextToken(tag, patternString); 44 * int typeOrCp = AffixPatternUtils.getTypeOrCp(tag); 45 * switch (typeOrCp) { 46 * case AffixPatternUtils.TYPE_MINUS_SIGN: 47 * // Current token is a minus sign. 48 * break; 49 * case AffixPatternUtils.TYPE_PLUS_SIGN: 50 * // Current token is a plus sign. 51 * break; 52 * case AffixPatternUtils.TYPE_PERCENT: 53 * // Current token is a percent sign. 54 * break; 55 * // ... other types ... 56 * default: 57 * // Current token is an arbitrary code point. 58 * // The variable typeOrCp is the code point. 59 * break; 60 * } 61 * } 62 * </pre> 63 * @hide exposed on OHOS 64 */ 65 public class AffixUtils { 66 67 private static final int STATE_BASE = 0; 68 private static final int STATE_FIRST_QUOTE = 1; 69 private static final int STATE_INSIDE_QUOTE = 2; 70 private static final int STATE_AFTER_QUOTE = 3; 71 private static final int STATE_FIRST_CURR = 4; 72 private static final int STATE_SECOND_CURR = 5; 73 private static final int STATE_THIRD_CURR = 6; 74 private static final int STATE_FOURTH_CURR = 7; 75 private static final int STATE_FIFTH_CURR = 8; 76 private static final int STATE_OVERFLOW_CURR = 9; 77 78 /** Represents a literal character; the value is stored in the code point field. */ 79 private static final int TYPE_CODEPOINT = 0; 80 81 /** Represents a minus sign symbol '-'. */ 82 public static final int TYPE_MINUS_SIGN = -1; 83 84 /** Represents a plus sign symbol '+'. */ 85 public static final int TYPE_PLUS_SIGN = -2; 86 87 /** Represents a percent sign symbol '%'. */ 88 public static final int TYPE_PERCENT = -3; 89 90 /** Represents a permille sign symbol '‰'. */ 91 public static final int TYPE_PERMILLE = -4; 92 93 /** Represents a single currency symbol '¤'. */ 94 public static final int TYPE_CURRENCY_SINGLE = -5; 95 96 /** Represents a double currency symbol '¤¤'. */ 97 public static final int TYPE_CURRENCY_DOUBLE = -6; 98 99 /** Represents a triple currency symbol '¤¤¤'. */ 100 public static final int TYPE_CURRENCY_TRIPLE = -7; 101 102 /** Represents a quadruple currency symbol '¤¤¤¤'. */ 103 public static final int TYPE_CURRENCY_QUAD = -8; 104 105 /** Represents a quintuple currency symbol '¤¤¤¤¤'. */ 106 public static final int TYPE_CURRENCY_QUINT = -9; 107 108 /** Represents a sequence of six or more currency symbols. */ 109 public static final int TYPE_CURRENCY_OVERFLOW = -15; 110 111 /** 112 * @hide exposed on OHOS 113 */ 114 public static interface SymbolProvider { getSymbol(int type)115 public CharSequence getSymbol(int type); 116 } 117 118 /** 119 * @hide exposed on OHOS 120 */ 121 public static interface TokenConsumer { consumeToken(int typeOrCp)122 public void consumeToken(int typeOrCp); 123 } 124 125 /** 126 * Estimates the number of code points present in an unescaped version of the affix pattern string 127 * (one that would be returned by {@link #unescape}), assuming that all interpolated symbols consume 128 * one code point and that currencies consume as many code points as their symbol width. Used for 129 * computing padding width. 130 * 131 * @param patternString 132 * The original string whose width will be estimated. 133 * @return The length of the unescaped string. 134 */ estimateLength(CharSequence patternString)135 public static int estimateLength(CharSequence patternString) { 136 if (patternString == null) 137 return 0; 138 int state = STATE_BASE; 139 int offset = 0; 140 int length = 0; 141 for (; offset < patternString.length();) { 142 int cp = Character.codePointAt(patternString, offset); 143 144 switch (state) { 145 case STATE_BASE: 146 if (cp == '\'') { 147 // First quote 148 state = STATE_FIRST_QUOTE; 149 } else { 150 // Unquoted symbol 151 length++; 152 } 153 break; 154 case STATE_FIRST_QUOTE: 155 if (cp == '\'') { 156 // Repeated quote 157 length++; 158 state = STATE_BASE; 159 } else { 160 // Quoted code point 161 length++; 162 state = STATE_INSIDE_QUOTE; 163 } 164 break; 165 case STATE_INSIDE_QUOTE: 166 if (cp == '\'') { 167 // End of quoted sequence 168 state = STATE_AFTER_QUOTE; 169 } else { 170 // Quoted code point 171 length++; 172 } 173 break; 174 case STATE_AFTER_QUOTE: 175 if (cp == '\'') { 176 // Double quote inside of quoted sequence 177 length++; 178 state = STATE_INSIDE_QUOTE; 179 } else { 180 // Unquoted symbol 181 length++; 182 } 183 break; 184 default: 185 throw new AssertionError(); 186 } 187 188 offset += Character.charCount(cp); 189 } 190 191 switch (state) { 192 case STATE_FIRST_QUOTE: 193 case STATE_INSIDE_QUOTE: 194 throw new IllegalArgumentException("Unterminated quote: \"" + patternString + "\""); 195 default: 196 break; 197 } 198 199 return length; 200 } 201 202 /** 203 * Takes a string and escapes (quotes) characters that have special meaning in the affix pattern 204 * syntax. This function does not reverse-lookup symbols. 205 * 206 * <p> 207 * Example input: "-$x"; example output: "'-'$x" 208 * 209 * @param input 210 * The string to be escaped. 211 * @param output 212 * The string builder to which to append the escaped string. 213 * @return The number of chars (UTF-16 code units) appended to the output. 214 */ escape(CharSequence input, StringBuilder output)215 public static int escape(CharSequence input, StringBuilder output) { 216 if (input == null) 217 return 0; 218 int state = STATE_BASE; 219 int offset = 0; 220 int startLength = output.length(); 221 for (; offset < input.length();) { 222 int cp = Character.codePointAt(input, offset); 223 224 switch (cp) { 225 case '\'': 226 output.append("''"); 227 break; 228 229 case '-': 230 case '+': 231 case '%': 232 case '‰': 233 case '¤': 234 if (state == STATE_BASE) { 235 output.append('\''); 236 output.appendCodePoint(cp); 237 state = STATE_INSIDE_QUOTE; 238 } else { 239 output.appendCodePoint(cp); 240 } 241 break; 242 243 default: 244 if (state == STATE_INSIDE_QUOTE) { 245 output.append('\''); 246 output.appendCodePoint(cp); 247 state = STATE_BASE; 248 } else { 249 output.appendCodePoint(cp); 250 } 251 break; 252 } 253 offset += Character.charCount(cp); 254 } 255 256 if (state == STATE_INSIDE_QUOTE) { 257 output.append('\''); 258 } 259 260 return output.length() - startLength; 261 } 262 263 /** Version of {@link #escape} that returns a String, or null if input is null. */ escape(CharSequence input)264 public static String escape(CharSequence input) { 265 if (input == null) 266 return null; 267 StringBuilder sb = new StringBuilder(); 268 escape(input, sb); 269 return sb.toString(); 270 } 271 getFieldForType(int type)272 public static final NumberFormat.Field getFieldForType(int type) { 273 switch (type) { 274 case TYPE_MINUS_SIGN: 275 return NumberFormat.Field.SIGN; 276 case TYPE_PLUS_SIGN: 277 return NumberFormat.Field.SIGN; 278 case TYPE_PERCENT: 279 return NumberFormat.Field.PERCENT; 280 case TYPE_PERMILLE: 281 return NumberFormat.Field.PERMILLE; 282 case TYPE_CURRENCY_SINGLE: 283 return NumberFormat.Field.CURRENCY; 284 case TYPE_CURRENCY_DOUBLE: 285 return NumberFormat.Field.CURRENCY; 286 case TYPE_CURRENCY_TRIPLE: 287 return NumberFormat.Field.CURRENCY; 288 case TYPE_CURRENCY_QUAD: 289 return NumberFormat.Field.CURRENCY; 290 case TYPE_CURRENCY_QUINT: 291 return NumberFormat.Field.CURRENCY; 292 case TYPE_CURRENCY_OVERFLOW: 293 return NumberFormat.Field.CURRENCY; 294 default: 295 throw new AssertionError(); 296 } 297 } 298 299 /** 300 * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", "‰", and "¤" 301 * with the corresponding symbols provided by the {@link SymbolProvider}, and inserts the result into 302 * the FormattedStringBuilder at the requested location. 303 * 304 * <p> 305 * Example input: "'-'¤x"; example output: "-$x" 306 * 307 * @param affixPattern 308 * The original string to be unescaped. 309 * @param output 310 * The FormattedStringBuilder to mutate with the result. 311 * @param position 312 * The index into the FormattedStringBuilder to insert the the string. 313 * @param provider 314 * An object to generate locale symbols. 315 * @return The length of the string added to affixPattern. 316 */ unescape( CharSequence affixPattern, FormattedStringBuilder output, int position, SymbolProvider provider, NumberFormat.Field field)317 public static int unescape( 318 CharSequence affixPattern, 319 FormattedStringBuilder output, 320 int position, 321 SymbolProvider provider, 322 NumberFormat.Field field) { 323 assert affixPattern != null; 324 int length = 0; 325 long tag = 0L; 326 while (hasNext(tag, affixPattern)) { 327 tag = nextToken(tag, affixPattern); 328 int typeOrCp = getTypeOrCp(tag); 329 if (typeOrCp == TYPE_CURRENCY_OVERFLOW) { 330 // Don't go to the provider for this special case 331 length += output.insertCodePoint(position + length, 0xFFFD, NumberFormat.Field.CURRENCY); 332 } else if (typeOrCp < 0) { 333 length += output.insert(position + length, 334 provider.getSymbol(typeOrCp), 335 getFieldForType(typeOrCp)); 336 } else { 337 length += output.insertCodePoint(position + length, typeOrCp, field); 338 } 339 } 340 return length; 341 } 342 343 /** 344 * Sames as {@link #unescape}, but only calculates the length or code point count. More efficient 345 * than {@link #unescape} if you only need the length but not the string itself. 346 * 347 * @param affixPattern 348 * The original string to be unescaped. 349 * @param lengthOrCount 350 * true to count length (UTF-16 code units); false to count code points 351 * @param provider 352 * An object to generate locale symbols. 353 * @return The number of code points in the unescaped string. 354 */ unescapedCount( CharSequence affixPattern, boolean lengthOrCount, SymbolProvider provider)355 public static int unescapedCount( 356 CharSequence affixPattern, 357 boolean lengthOrCount, 358 SymbolProvider provider) { 359 int length = 0; 360 long tag = 0L; 361 while (hasNext(tag, affixPattern)) { 362 tag = nextToken(tag, affixPattern); 363 int typeOrCp = getTypeOrCp(tag); 364 if (typeOrCp == TYPE_CURRENCY_OVERFLOW) { 365 // U+FFFD is one char 366 length += 1; 367 } else if (typeOrCp < 0) { 368 CharSequence symbol = provider.getSymbol(typeOrCp); 369 length += lengthOrCount ? symbol.length() 370 : Character.codePointCount(symbol, 0, symbol.length()); 371 } else { 372 length += lengthOrCount ? Character.charCount(typeOrCp) : 1; 373 } 374 } 375 return length; 376 } 377 378 /** 379 * Checks whether the given affix pattern contains at least one token of the given type, which is one 380 * of the constants "TYPE_" in {@link AffixUtils}. 381 * 382 * @param affixPattern 383 * The affix pattern to check. 384 * @param type 385 * The token type. 386 * @return true if the affix pattern contains the given token type; false otherwise. 387 */ containsType(CharSequence affixPattern, int type)388 public static boolean containsType(CharSequence affixPattern, int type) { 389 if (affixPattern == null || affixPattern.length() == 0) { 390 return false; 391 } 392 long tag = 0L; 393 while (hasNext(tag, affixPattern)) { 394 tag = nextToken(tag, affixPattern); 395 if (getTypeOrCp(tag) == type) { 396 return true; 397 } 398 } 399 return false; 400 } 401 402 /** 403 * Checks whether the specified affix pattern has any unquoted currency symbols ("¤"). 404 * 405 * @param affixPattern 406 * The string to check for currency symbols. 407 * @return true if the literal has at least one unquoted currency symbol; false otherwise. 408 */ hasCurrencySymbols(CharSequence affixPattern)409 public static boolean hasCurrencySymbols(CharSequence affixPattern) { 410 if (affixPattern == null || affixPattern.length() == 0) 411 return false; 412 long tag = 0L; 413 while (hasNext(tag, affixPattern)) { 414 tag = nextToken(tag, affixPattern); 415 int typeOrCp = getTypeOrCp(tag); 416 if (typeOrCp < 0 && getFieldForType(typeOrCp) == NumberFormat.Field.CURRENCY) { 417 return true; 418 } 419 } 420 return false; 421 } 422 423 /** 424 * Replaces all occurrences of tokens with the given type with the given replacement char. 425 * 426 * @param affixPattern 427 * The source affix pattern (does not get modified). 428 * @param type 429 * The token type. 430 * @param replacementChar 431 * The char to substitute in place of chars of the given token type. 432 * @return A string containing the new affix pattern. 433 */ replaceType(CharSequence affixPattern, int type, char replacementChar)434 public static String replaceType(CharSequence affixPattern, int type, char replacementChar) { 435 if (affixPattern == null || affixPattern.length() == 0) 436 return ""; 437 char[] chars = affixPattern.toString().toCharArray(); 438 long tag = 0L; 439 while (hasNext(tag, affixPattern)) { 440 tag = nextToken(tag, affixPattern); 441 if (getTypeOrCp(tag) == type) { 442 int offset = getOffset(tag); 443 chars[offset - 1] = replacementChar; 444 } 445 } 446 return new String(chars); 447 } 448 449 /** 450 * Returns whether the given affix pattern contains only symbols and ignorables as defined by the 451 * given ignorables set. 452 */ containsOnlySymbolsAndIgnorables( CharSequence affixPattern, UnicodeSet ignorables)453 public static boolean containsOnlySymbolsAndIgnorables( 454 CharSequence affixPattern, 455 UnicodeSet ignorables) { 456 if (affixPattern == null) { 457 return true; 458 } 459 long tag = 0L; 460 while (hasNext(tag, affixPattern)) { 461 tag = nextToken(tag, affixPattern); 462 int typeOrCp = getTypeOrCp(tag); 463 if (typeOrCp >= 0 && !ignorables.contains(typeOrCp)) { 464 return false; 465 } 466 } 467 return true; 468 } 469 470 /** 471 * Iterates over the affix pattern, calling the TokenConsumer for each token. 472 */ iterateWithConsumer(CharSequence affixPattern, TokenConsumer consumer)473 public static void iterateWithConsumer(CharSequence affixPattern, TokenConsumer consumer) { 474 assert affixPattern != null; 475 long tag = 0L; 476 while (hasNext(tag, affixPattern)) { 477 tag = nextToken(tag, affixPattern); 478 int typeOrCp = getTypeOrCp(tag); 479 consumer.consumeToken(typeOrCp); 480 } 481 } 482 483 /** 484 * Returns the next token from the affix pattern. 485 * 486 * @param tag 487 * A bitmask used for keeping track of state from token to token. The initial value should 488 * be 0L. 489 * @param patternString 490 * The affix pattern. 491 * @return The bitmask tag to pass to the next call of this method to retrieve the following token 492 * (never negative), or -1 if there were no more tokens in the affix pattern. 493 * @see #hasNext 494 */ nextToken(long tag, CharSequence patternString)495 private static long nextToken(long tag, CharSequence patternString) { 496 int offset = getOffset(tag); 497 int state = getState(tag); 498 for (; offset < patternString.length();) { 499 int cp = Character.codePointAt(patternString, offset); 500 int count = Character.charCount(cp); 501 502 switch (state) { 503 case STATE_BASE: 504 switch (cp) { 505 case '\'': 506 state = STATE_FIRST_QUOTE; 507 offset += count; 508 // continue to the next code point 509 break; 510 case '-': 511 return makeTag(offset + count, TYPE_MINUS_SIGN, STATE_BASE, 0); 512 case '+': 513 return makeTag(offset + count, TYPE_PLUS_SIGN, STATE_BASE, 0); 514 case '%': 515 return makeTag(offset + count, TYPE_PERCENT, STATE_BASE, 0); 516 case '‰': 517 return makeTag(offset + count, TYPE_PERMILLE, STATE_BASE, 0); 518 case '¤': 519 state = STATE_FIRST_CURR; 520 offset += count; 521 // continue to the next code point 522 break; 523 default: 524 return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp); 525 } 526 break; 527 case STATE_FIRST_QUOTE: 528 if (cp == '\'') { 529 return makeTag(offset + count, TYPE_CODEPOINT, STATE_BASE, cp); 530 } else { 531 return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); 532 } 533 case STATE_INSIDE_QUOTE: 534 if (cp == '\'') { 535 state = STATE_AFTER_QUOTE; 536 offset += count; 537 // continue to the next code point 538 break; 539 } else { 540 return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); 541 } 542 case STATE_AFTER_QUOTE: 543 if (cp == '\'') { 544 return makeTag(offset + count, TYPE_CODEPOINT, STATE_INSIDE_QUOTE, cp); 545 } else { 546 state = STATE_BASE; 547 // re-evaluate this code point 548 break; 549 } 550 case STATE_FIRST_CURR: 551 if (cp == '¤') { 552 state = STATE_SECOND_CURR; 553 offset += count; 554 // continue to the next code point 555 break; 556 } else { 557 return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0); 558 } 559 case STATE_SECOND_CURR: 560 if (cp == '¤') { 561 state = STATE_THIRD_CURR; 562 offset += count; 563 // continue to the next code point 564 break; 565 } else { 566 return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0); 567 } 568 case STATE_THIRD_CURR: 569 if (cp == '¤') { 570 state = STATE_FOURTH_CURR; 571 offset += count; 572 // continue to the next code point 573 break; 574 } else { 575 return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0); 576 } 577 case STATE_FOURTH_CURR: 578 if (cp == '¤') { 579 state = STATE_FIFTH_CURR; 580 offset += count; 581 // continue to the next code point 582 break; 583 } else { 584 return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0); 585 } 586 case STATE_FIFTH_CURR: 587 if (cp == '¤') { 588 state = STATE_OVERFLOW_CURR; 589 offset += count; 590 // continue to the next code point 591 break; 592 } else { 593 return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0); 594 } 595 case STATE_OVERFLOW_CURR: 596 if (cp == '¤') { 597 offset += count; 598 // continue to the next code point and loop back to this state 599 break; 600 } else { 601 return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0); 602 } 603 default: 604 throw new AssertionError(); 605 } 606 } 607 // End of string 608 switch (state) { 609 case STATE_BASE: 610 // No more tokens in string. 611 return -1L; 612 case STATE_FIRST_QUOTE: 613 case STATE_INSIDE_QUOTE: 614 // For consistent behavior with the JDK and ICU 58, throw an exception here. 615 throw new IllegalArgumentException( 616 "Unterminated quote in pattern affix: \"" + patternString + "\""); 617 case STATE_AFTER_QUOTE: 618 // No more tokens in string. 619 return -1L; 620 case STATE_FIRST_CURR: 621 return makeTag(offset, TYPE_CURRENCY_SINGLE, STATE_BASE, 0); 622 case STATE_SECOND_CURR: 623 return makeTag(offset, TYPE_CURRENCY_DOUBLE, STATE_BASE, 0); 624 case STATE_THIRD_CURR: 625 return makeTag(offset, TYPE_CURRENCY_TRIPLE, STATE_BASE, 0); 626 case STATE_FOURTH_CURR: 627 return makeTag(offset, TYPE_CURRENCY_QUAD, STATE_BASE, 0); 628 case STATE_FIFTH_CURR: 629 return makeTag(offset, TYPE_CURRENCY_QUINT, STATE_BASE, 0); 630 case STATE_OVERFLOW_CURR: 631 return makeTag(offset, TYPE_CURRENCY_OVERFLOW, STATE_BASE, 0); 632 default: 633 throw new AssertionError(); 634 } 635 } 636 637 /** 638 * Returns whether the affix pattern string has any more tokens to be retrieved from a call to 639 * {@link #nextToken}. 640 * 641 * @param tag 642 * The bitmask tag of the previous token, as returned by {@link #nextToken}. 643 * @param string 644 * The affix pattern. 645 * @return true if there are more tokens to consume; false otherwise. 646 */ hasNext(long tag, CharSequence string)647 private static boolean hasNext(long tag, CharSequence string) { 648 assert tag >= 0; 649 int state = getState(tag); 650 int offset = getOffset(tag); 651 // Special case: the last character in string is an end quote. 652 if (state == STATE_INSIDE_QUOTE 653 && offset == string.length() - 1 654 && string.charAt(offset) == '\'') { 655 return false; 656 } else if (state != STATE_BASE) { 657 return true; 658 } else { 659 return offset < string.length(); 660 } 661 } 662 663 /** 664 * This function helps determine the identity of the token consumed by {@link #nextToken}. Converts 665 * from a bitmask tag, based on a call to {@link #nextToken}, to its corresponding symbol type or 666 * code point. 667 * 668 * @param tag 669 * The bitmask tag of the current token, as returned by {@link #nextToken}. 670 * @return If less than zero, a symbol type corresponding to one of the <code>TYPE_</code> constants, 671 * such as {@link #TYPE_MINUS_SIGN}. If greater than or equal to zero, a literal code point. 672 */ getTypeOrCp(long tag)673 private static int getTypeOrCp(long tag) { 674 assert tag >= 0; 675 int type = getType(tag); 676 return (type == TYPE_CODEPOINT) ? getCodePoint(tag) : -type; 677 } 678 679 /** 680 * Encodes the given values into a 64-bit tag. 681 * 682 * <ul> 683 * <li>Bits 0-31 => offset (int32) 684 * <li>Bits 32-35 => type (uint4) 685 * <li>Bits 36-39 => state (uint4) 686 * <li>Bits 40-60 => code point (uint21) 687 * <li>Bits 61-63 => unused 688 * </ul> 689 */ makeTag(int offset, int type, int state, int cp)690 private static long makeTag(int offset, int type, int state, int cp) { 691 long tag = 0L; 692 tag |= offset; 693 tag |= (-(long) type) << 32; 694 tag |= ((long) state) << 36; 695 tag |= ((long) cp) << 40; 696 assert tag >= 0; 697 return tag; 698 } 699 getOffset(long tag)700 private static int getOffset(long tag) { 701 return (int) (tag & 0xffffffff); 702 } 703 getType(long tag)704 private static int getType(long tag) { 705 return (int) ((tag >>> 32) & 0xf); 706 } 707 getState(long tag)708 private static int getState(long tag) { 709 return (int) ((tag >>> 36) & 0xf); 710 } 711 getCodePoint(long tag)712 private static int getCodePoint(long tag) { 713 return (int) (tag >>> 40); 714 } 715 } 716