001/******************************************************************************* 002The MIT License (MIT) 003 004Copyright (c) 2024 KILLCODING.COM 005 006Permission is hereby granted, free of charge, to any person obtaining a copy 007of this software and associated documentation files (the "Software"), to deal 008in the Software without restriction, including without limitation the rights 009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010copies of the Software, and to permit persons to whom the Software is 011furnished to do so, subject to the following conditions: 012 013The above copyright notice and this permission notice shall be included in 014all copies or substantial portions of the Software. 015 016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 022THE SOFTWARE. 023*****************************************************************************/ 024package com.killcoding.tool; 025 026import java.io.CharArrayWriter; 027import java.io.IOException; 028import java.io.Reader; 029import java.io.Writer; 030import java.util.Arrays; 031 032final class CodeEscapeUtil { 033 034 035 private static final char ESCAPE_PREFIX = '\\'; 036 private static final char ESCAPE_UHEXA_PREFIX2 = 'u'; 037 private static final char[] ESCAPE_UHEXA_PREFIX = "\\u".toCharArray(); 038 039 private static char[] HEXA_CHARS_UPPER = "0123456789ABCDEF".toCharArray(); 040 private static char[] HEXA_CHARS_LOWER = "0123456789abcdef".toCharArray(); 041 042 private static int SEC_CHARS_LEN = '\\' + 1; 043 private static char SEC_CHARS_NO_SEC = '*'; 044 private static char[] SEC_CHARS; 045 046 private static final char ESCAPE_LEVELS_LEN = 0x9f + 2; 047 private static final byte[] ESCAPE_LEVELS; 048 049 050 051 static { 052 053 SEC_CHARS = new char[SEC_CHARS_LEN]; 054 Arrays.fill(SEC_CHARS,SEC_CHARS_NO_SEC); 055 SEC_CHARS[0x08] = 'b'; 056 SEC_CHARS[0x09] = 't'; 057 SEC_CHARS[0x0A] = 'n'; 058 SEC_CHARS[0x0C] = 'f'; 059 SEC_CHARS[0x0D] = 'r'; 060 SEC_CHARS[0x22] = '"'; 061 062 SEC_CHARS[0x27] = '\''; 063 SEC_CHARS[0x5C] = '\\'; 064 065 ESCAPE_LEVELS = new byte[ESCAPE_LEVELS_LEN]; 066 067 Arrays.fill(ESCAPE_LEVELS, (byte)3); 068 069 for (char c = 0x80; c < ESCAPE_LEVELS_LEN; c++) { 070 ESCAPE_LEVELS[c] = 2; 071 } 072 073 for (char c = 'A'; c <= 'Z'; c++) { 074 ESCAPE_LEVELS[c] = 4; 075 } 076 for (char c = 'a'; c <= 'z'; c++) { 077 ESCAPE_LEVELS[c] = 4; 078 } 079 for (char c = '0'; c <= '9'; c++) { 080 ESCAPE_LEVELS[c] = 4; 081 } 082 083 ESCAPE_LEVELS[0x08] = 1; 084 ESCAPE_LEVELS[0x09] = 1; 085 ESCAPE_LEVELS[0x0A] = 1; 086 ESCAPE_LEVELS[0x0C] = 1; 087 ESCAPE_LEVELS[0x0D] = 1; 088 ESCAPE_LEVELS[0x22] = 1; 089 090 ESCAPE_LEVELS[0x27] = 3; 091 ESCAPE_LEVELS[0x5C] = 1; 092 093 for (char c = 0x00; c <= 0x1F; c++) { 094 ESCAPE_LEVELS[c] = 1; 095 } 096 for (char c = 0x7F; c <= 0x9F; c++) { 097 ESCAPE_LEVELS[c] = 1; 098 } 099 100 } 101 102 103 104 private CodeEscapeUtil() { 105 super(); 106 } 107 108 static char[] toUHexa(final int codepoint) { 109 final char[] result = new char[4]; 110 result[3] = HEXA_CHARS_UPPER[codepoint % 0x10]; 111 result[2] = HEXA_CHARS_UPPER[(codepoint >>> 4) % 0x10]; 112 result[1] = HEXA_CHARS_UPPER[(codepoint >>> 8) % 0x10]; 113 result[0] = HEXA_CHARS_UPPER[(codepoint >>> 12) % 0x10]; 114 return result; 115 } 116 117 static String escape(final String text, final CodeEscapeLevel escapeLevel) { 118 119 if (text == null) { 120 return null; 121 } 122 123 final int level = escapeLevel.getEscapeLevel(); 124 125 StringBuilder strBuilder = null; 126 127 final int offset = 0; 128 final int max = text.length(); 129 130 int readOffset = offset; 131 132 for (int i = offset; i < max; i++) { 133 134 final int codepoint = Character.codePointAt(text, i); 135 136 if (codepoint <= (ESCAPE_LEVELS_LEN - 2) && level < ESCAPE_LEVELS[codepoint]) { 137 continue; 138 } 139 140 if (codepoint > (ESCAPE_LEVELS_LEN - 2) && level < ESCAPE_LEVELS[ESCAPE_LEVELS_LEN - 1]) { 141 142 if (Character.charCount(codepoint) > 1) { 143 i++; 144 } 145 146 continue; 147 148 } 149 150 if (strBuilder == null) { 151 strBuilder = new StringBuilder(max + 20); 152 } 153 154 if (i - readOffset > 0) { 155 strBuilder.append(text, readOffset, i); 156 } 157 158 if (Character.charCount(codepoint) > 1) { 159 i++; 160 } 161 162 readOffset = i + 1; 163 164 165 166 167 if (codepoint < SEC_CHARS_LEN) { 168 169 final char sec = SEC_CHARS[codepoint]; 170 171 if (sec != SEC_CHARS_NO_SEC) { 172 173 strBuilder.append(ESCAPE_PREFIX); 174 strBuilder.append(sec); 175 continue; 176 } 177 178 } 179 180 if (Character.charCount(codepoint) > 1) { 181 final char[] codepointChars = Character.toChars(codepoint); 182 strBuilder.append(ESCAPE_UHEXA_PREFIX); 183 strBuilder.append(toUHexa(codepointChars[0])); 184 strBuilder.append(ESCAPE_UHEXA_PREFIX); 185 strBuilder.append(toUHexa(codepointChars[1])); 186 continue; 187 } 188 189 strBuilder.append(ESCAPE_UHEXA_PREFIX); 190 strBuilder.append(toUHexa(codepoint)); 191 192 } 193 194 if (strBuilder == null) { 195 return text; 196 } 197 198 if (max - readOffset > 0) { 199 strBuilder.append(text, readOffset, max); 200 } 201 202 return strBuilder.toString(); 203 204 } 205 206 static void escape(final Reader reader, final Writer writer, final CodeEscapeLevel escapeLevel) 207 throws IOException { 208 209 if (reader == null) { 210 return; 211 } 212 213 final int level = escapeLevel.getEscapeLevel(); 214 215 int c1, c2; 216 217 c2 = reader.read(); 218 219 while (c2 >= 0) { 220 221 c1 = c2; 222 c2 = reader.read(); 223 224 final int codepoint = codePointAt((char)c1, (char)c2); 225 226 if (codepoint <= (ESCAPE_LEVELS_LEN - 2) && level < ESCAPE_LEVELS[codepoint]) { 227 writer.write(c1); 228 continue; 229 } 230 231 if (codepoint > (ESCAPE_LEVELS_LEN - 2) && level < ESCAPE_LEVELS[ESCAPE_LEVELS_LEN - 1]) { 232 233 writer.write(c1); 234 235 if (Character.charCount(codepoint) > 1) { 236 writer.write(c2); 237 238 c1 = c2; 239 c2 = reader.read(); 240 241 } 242 243 continue; 244 245 } 246 247 248 if (Character.charCount(codepoint) > 1) { 249 c1 = c2; 250 c2 = reader.read(); 251 } 252 253 if (codepoint < SEC_CHARS_LEN) { 254 255 final char sec = SEC_CHARS[codepoint]; 256 257 if (sec != SEC_CHARS_NO_SEC) { 258 writer.write(ESCAPE_PREFIX); 259 writer.write(sec); 260 continue; 261 } 262 263 } 264 265 if (Character.charCount(codepoint) > 1) { 266 final char[] codepointChars = Character.toChars(codepoint); 267 writer.write(ESCAPE_UHEXA_PREFIX); 268 writer.write(toUHexa(codepointChars[0])); 269 writer.write(ESCAPE_UHEXA_PREFIX); 270 writer.write(toUHexa(codepointChars[1])); 271 continue; 272 } 273 274 writer.write(ESCAPE_UHEXA_PREFIX); 275 writer.write(toUHexa(codepoint)); 276 277 } 278 279 } 280 281 static void escape(final char[] text, final int offset, final int len, final Writer writer, 282 final CodeEscapeLevel escapeLevel) 283 throws IOException { 284 285 if (text == null || text.length == 0) { 286 return; 287 } 288 289 final int level = escapeLevel.getEscapeLevel(); 290 291 final int max = (offset + len); 292 293 int readOffset = offset; 294 295 for (int i = offset; i < max; i++) { 296 297 final int codepoint = Character.codePointAt(text, i); 298 299 if (codepoint <= (ESCAPE_LEVELS_LEN - 2) && level < ESCAPE_LEVELS[codepoint]) { 300 continue; 301 } 302 303 if (codepoint > (ESCAPE_LEVELS_LEN - 2) && level < ESCAPE_LEVELS[ESCAPE_LEVELS_LEN - 1]) { 304 305 if (Character.charCount(codepoint) > 1) { 306 i++; 307 } 308 309 continue; 310 311 } 312 313 if (i - readOffset > 0) { 314 writer.write(text, readOffset, (i - readOffset)); 315 } 316 317 if (Character.charCount(codepoint) > 1) { 318 i++; 319 } 320 321 readOffset = i + 1; 322 323 if (codepoint < SEC_CHARS_LEN) { 324 325 final char sec = SEC_CHARS[codepoint]; 326 327 if (sec != SEC_CHARS_NO_SEC) { 328 writer.write(ESCAPE_PREFIX); 329 writer.write(sec); 330 continue; 331 } 332 333 } 334 335 if (Character.charCount(codepoint) > 1) { 336 final char[] codepointChars = Character.toChars(codepoint); 337 writer.write(ESCAPE_UHEXA_PREFIX); 338 writer.write(toUHexa(codepointChars[0])); 339 writer.write(ESCAPE_UHEXA_PREFIX); 340 writer.write(toUHexa(codepointChars[1])); 341 continue; 342 } 343 344 writer.write(ESCAPE_UHEXA_PREFIX); 345 writer.write(toUHexa(codepoint)); 346 347 } 348 349 if (max - readOffset > 0) { 350 writer.write(text, readOffset, (max - readOffset)); 351 } 352 353 } 354 355 static int parseIntFromReference(final String text, final int start, final int end, final int radix) { 356 int result = 0; 357 for (int i = start; i < end; i++) { 358 final char c = text.charAt(i); 359 int n = -1; 360 for (int j = 0; j < HEXA_CHARS_UPPER.length; j++) { 361 if (c == HEXA_CHARS_UPPER[j] || c == HEXA_CHARS_LOWER[j]) { 362 n = j; 363 break; 364 } 365 } 366 result = (radix * result) + n; 367 } 368 return result; 369 } 370 371 static int parseIntFromReference(final char[] text, final int start, final int end, final int radix) { 372 int result = 0; 373 for (int i = start; i < end; i++) { 374 final char c = text[i]; 375 int n = -1; 376 for (int j = 0; j < HEXA_CHARS_UPPER.length; j++) { 377 if (c == HEXA_CHARS_UPPER[j] || c == HEXA_CHARS_LOWER[j]) { 378 n = j; 379 break; 380 } 381 } 382 result = (radix * result) + n; 383 } 384 return result; 385 } 386 387 static boolean isOctalEscape(final String text, final int start, final int end) { 388 389 if (start >= end) { 390 return false; 391 } 392 393 final char c1 = text.charAt(start); 394 if (c1 < '0' || c1 > '7') { 395 return false; 396 } 397 398 if (start + 1 >= end) { 399 return (c1 != '0'); 400 } 401 402 final char c2 = text.charAt(start + 1); 403 if (c2 < '0' || c2 > '7') { 404 return (c1 != '0'); 405 } 406 407 if (start + 2 >= end) { 408 return (c1 != '0' || c2 != '0'); 409 } 410 411 final char c3 = text.charAt(start + 2); 412 if (c3 < '0' || c3 > '7') { 413 return (c1 != '0' || c2 != '0'); 414 } 415 416 return (c1 != '0' || c2 != '0' || c3 != '0'); 417 418 } 419 420 421 static boolean isOctalEscape(final char[] text, final int start, final int end) { 422 423 if (start >= end) { 424 return false; 425 } 426 427 final char c1 = text[start]; 428 if (c1 < '0' || c1 > '7') { 429 return false; 430 } 431 432 if (start + 1 >= end) { 433 return (c1 != '0'); 434 } 435 436 final char c2 = text[start + 1]; 437 if (c2 < '0' || c2 > '7') { 438 return (c1 != '0'); 439 } 440 441 if (start + 2 >= end) { 442 return (c1 != '0' || c2 != '0'); 443 } 444 445 final char c3 = text[start + 2]; 446 if (c3 < '0' || c3 > '7') { 447 return (c1 != '0' || c2 != '0'); 448 } 449 450 return (c1 != '0' || c2 != '0' || c3 != '0'); 451 452 } 453 454 static String unicodeUnescape(final String text) { 455 456 if (text == null) { 457 return null; 458 } 459 460 StringBuilder strBuilder = null; 461 462 final int offset = 0; 463 final int max = text.length(); 464 465 int readOffset = offset; 466 int referenceOffset = offset; 467 468 for (int i = offset; i < max; i++) { 469 470 final char c = text.charAt(i); 471 472 473 if (c != ESCAPE_PREFIX || (i + 1) >= max) { 474 continue; 475 } 476 477 int codepoint = -1; 478 479 if (c == ESCAPE_PREFIX) { 480 481 final char c1 = text.charAt(i + 1); 482 483 if (c1 == ESCAPE_UHEXA_PREFIX2) { 484 485 int f = i + 2; 486 487 while (f < max) { 488 final char cf = text.charAt(f); 489 if (cf != ESCAPE_UHEXA_PREFIX2) { 490 break; 491 } 492 f++; 493 } 494 int s = f; 495 496 while (f < (s + 4) && f < max) { 497 final char cf = text.charAt(f); 498 if (!((cf >= '0' && cf <= '9') || (cf >= 'A' && cf <= 'F') || (cf >= 'a' && cf <= 'f'))) { 499 break; 500 } 501 f++; 502 } 503 504 if ((f - s) < 4) { 505 i++; 506 continue; 507 } 508 509 codepoint = parseIntFromReference(text, s, f, 16); 510 511 referenceOffset = f - 1; 512 513 } else { 514 515 i++; 516 continue; 517 518 } 519 520 } 521 522 if (strBuilder == null) { 523 strBuilder = new StringBuilder(max + 5); 524 } 525 526 if (i - readOffset > 0) { 527 strBuilder.append(text, readOffset, i); 528 } 529 530 i = referenceOffset; 531 readOffset = i + 1; 532 533 if (codepoint > '\uFFFF') { 534 strBuilder.append(Character.toChars(codepoint)); 535 } else { 536 strBuilder.append((char)codepoint); 537 } 538 539 } 540 541 542 if (strBuilder == null) { 543 return text; 544 } 545 546 if (max - readOffset > 0) { 547 strBuilder.append(text, readOffset, max); 548 } 549 550 return strBuilder.toString(); 551 552 } 553 554 555 556 static boolean requiresUnicodeUnescape(final char[] text, final int offset, final int len) { 557 558 if (text == null) { 559 return false; 560 } 561 562 final int max = (offset + len); 563 564 for (int i = offset; i < max; i++) { 565 566 final char c = text[i]; 567 568 if (c != ESCAPE_PREFIX || (i + 1) >= max) { 569 continue; 570 } 571 572 if (c == ESCAPE_PREFIX) { 573 574 final char c1 = text[i + 1]; 575 576 if (c1 == ESCAPE_UHEXA_PREFIX2) { 577 return true; 578 } 579 580 } 581 582 } 583 584 return false; 585 586 } 587 588 589 590 static void unicodeUnescape(final char[] text, final int offset, final int len, final Writer writer) 591 throws IOException { 592 593 if (text == null) { 594 return; 595 } 596 597 final int max = (offset + len); 598 599 int readOffset = offset; 600 int referenceOffset = offset; 601 602 for (int i = offset; i < max; i++) { 603 604 final char c = text[i]; 605 606 if (c != ESCAPE_PREFIX || (i + 1) >= max) { 607 continue; 608 } 609 610 int codepoint = -1; 611 612 if (c == ESCAPE_PREFIX) { 613 614 final char c1 = text[i + 1]; 615 616 if (c1 == ESCAPE_UHEXA_PREFIX2) { 617 618 int f = i + 2; 619 620 while (f < max) { 621 final char cf = text[f]; 622 if (cf != ESCAPE_UHEXA_PREFIX2) { 623 break; 624 } 625 f++; 626 } 627 int s = f; 628 629 while (f < (s + 4) && f < max) { 630 final char cf = text[f]; 631 if (!((cf >= '0' && cf <= '9') || (cf >= 'A' && cf <= 'F') || (cf >= 'a' && cf <= 'f'))) { 632 break; 633 } 634 f++; 635 } 636 637 if ((f - s) < 4) { 638 i++; 639 continue; 640 } 641 642 codepoint = parseIntFromReference(text, s, f, 16); 643 644 referenceOffset = f - 1; 645 646 647 } else { 648 i++; 649 continue; 650 651 } 652 653 } 654 655 if (i - readOffset > 0) { 656 writer.write(text, readOffset, (i - readOffset)); 657 } 658 659 i = referenceOffset; 660 readOffset = i + 1; 661 662 if (codepoint > '\uFFFF') { 663 writer.write(Character.toChars(codepoint)); 664 } else { 665 writer.write((char) codepoint); 666 } 667 668 } 669 670 671 if (max - readOffset > 0) { 672 writer.write(text, readOffset, (max - readOffset)); 673 } 674 675 } 676 677 static String unescape(final String text) { 678 679 if (text == null) { 680 return null; 681 } 682 683 final String unicodeEscapedText = unicodeUnescape(text); 684 685 StringBuilder strBuilder = null; 686 687 final int offset = 0; 688 final int max = unicodeEscapedText.length(); 689 690 int readOffset = offset; 691 int referenceOffset = offset; 692 693 for (int i = offset; i < max; i++) { 694 695 final char c = unicodeEscapedText.charAt(i); 696 697 if (c != ESCAPE_PREFIX || (i + 1) >= max) { 698 continue; 699 } 700 701 int codepoint = -1; 702 703 if (c == ESCAPE_PREFIX) { 704 705 final char c1 = unicodeEscapedText.charAt(i + 1); 706 707 switch (c1) { 708 case '0': if (!isOctalEscape(unicodeEscapedText,i + 1,max)) { codepoint = 0x00; referenceOffset = i + 1; }; break; 709 case 'b': codepoint = 0x08; referenceOffset = i + 1; break; 710 case 't': codepoint = 0x09; referenceOffset = i + 1; break; 711 case 'n': codepoint = 0x0A; referenceOffset = i + 1; break; 712 case 'f': codepoint = 0x0C; referenceOffset = i + 1; break; 713 case 'r': codepoint = 0x0D; referenceOffset = i + 1; break; 714 case '"': codepoint = 0x22; referenceOffset = i + 1; break; 715 case '\'': codepoint = 0x27; referenceOffset = i + 1; break; 716 case '\\': codepoint = 0x5C; referenceOffset = i + 1; break; 717 } 718 719 if (codepoint == -1) { 720 721 if (c1 >= '0' && c1 <= '7') { 722 723 int f = i + 2; 724 while (f < (i + 4) && f < max) { 725 final char cf = unicodeEscapedText.charAt(f); 726 if (!(cf >= '0' && cf <= '7')) { 727 break; 728 } 729 f++; 730 } 731 732 codepoint = parseIntFromReference(unicodeEscapedText, i + 1, f, 8); 733 734 if (codepoint > 0xFF) { 735 codepoint = parseIntFromReference(unicodeEscapedText, i + 1, f - 1, 8); 736 referenceOffset = f - 2; 737 } else { 738 referenceOffset = f - 1; 739 } 740 741 } else { 742 i++; 743 continue; 744 745 } 746 747 } 748 749 } 750 751 if (strBuilder == null) { 752 strBuilder = new StringBuilder(max + 5); 753 } 754 755 if (i - readOffset > 0) { 756 strBuilder.append(unicodeEscapedText, readOffset, i); 757 } 758 759 i = referenceOffset; 760 readOffset = i + 1; 761 762 if (codepoint > '\uFFFF') { 763 strBuilder.append(Character.toChars(codepoint)); 764 } else { 765 strBuilder.append((char)codepoint); 766 } 767 768 } 769 770 if (strBuilder == null) { 771 return unicodeEscapedText; 772 } 773 774 if (max - readOffset > 0) { 775 strBuilder.append(unicodeEscapedText, readOffset, max); 776 } 777 778 return strBuilder.toString(); 779 780 } 781 782 static void unescape(final Reader reader, final Writer writer) throws IOException { 783 784 if (reader == null) { 785 return; 786 } 787 788 char[] buffer = new char[20]; 789 790 int read = reader.read(buffer, 0, buffer.length); 791 if (read < 0) { 792 return; 793 } 794 795 int bufferSize = read; 796 797 while (bufferSize > 0 || read >= 0) { 798 799 int nonEscCounter = 0; 800 801 int n = bufferSize; 802 while (nonEscCounter < 8 && n-- != 0) { 803 if (buffer[n] == '\\') { 804 nonEscCounter = 0; 805 continue; 806 } 807 nonEscCounter++; 808 } 809 810 if (nonEscCounter < 8 && read >= 0) { 811 812 if (bufferSize == buffer.length) { 813 final char[] newBuffer = new char[buffer.length + (buffer.length / 2)]; 814 System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); 815 buffer = newBuffer; 816 } 817 818 read = reader.read(buffer, bufferSize, (buffer.length - bufferSize)); 819 if (read >= 0) { 820 bufferSize += read; 821 } 822 823 continue; 824 825 } 826 827 n = (n < 0? bufferSize : n + nonEscCounter); 828 829 unescape(buffer, 0, n, writer); 830 831 System.arraycopy(buffer, n, buffer, 0, (bufferSize - n)); 832 bufferSize -= n; 833 834 read = reader.read(buffer, bufferSize, (buffer.length - bufferSize)); 835 if (read >= 0) { 836 bufferSize += read; 837 } 838 839 } 840 841 } 842 843 static void unescape(final char[] text, final int offset, final int len, final Writer writer) 844 throws IOException { 845 846 if (text == null) { 847 return; 848 } 849 850 char[] unicodeEscapedText = text; 851 int unicodeEscapedOffset = offset; 852 int unicodeEscapedLen = len; 853 if (requiresUnicodeUnescape(text, offset, len)) { 854 final CharArrayWriter charArrayWriter = new CharArrayWriter(len + 2); 855 unicodeUnescape(text, offset, len, charArrayWriter); 856 unicodeEscapedText = charArrayWriter.toCharArray(); 857 unicodeEscapedOffset = 0; 858 unicodeEscapedLen = unicodeEscapedText.length; 859 } 860 861 final int max = (unicodeEscapedOffset + unicodeEscapedLen); 862 863 int readOffset = unicodeEscapedOffset; 864 int referenceOffset = unicodeEscapedOffset; 865 866 for (int i = unicodeEscapedOffset; i < max; i++) { 867 868 final char c = unicodeEscapedText[i]; 869 870 if (c != ESCAPE_PREFIX || (i + 1) >= max) { 871 continue; 872 } 873 874 int codepoint = -1; 875 876 if (c == ESCAPE_PREFIX) { 877 878 final char c1 = unicodeEscapedText[i + 1]; 879 880 switch (c1) { 881 case '0': if (!isOctalEscape(unicodeEscapedText,i + 1,max)) { codepoint = 0x00; referenceOffset = i + 1; }; break; 882 case 'b': codepoint = 0x08; referenceOffset = i + 1; break; 883 case 't': codepoint = 0x09; referenceOffset = i + 1; break; 884 case 'n': codepoint = 0x0A; referenceOffset = i + 1; break; 885 case 'f': codepoint = 0x0C; referenceOffset = i + 1; break; 886 case 'r': codepoint = 0x0D; referenceOffset = i + 1; break; 887 case '"': codepoint = 0x22; referenceOffset = i + 1; break; 888 case '\'': codepoint = 0x27; referenceOffset = i + 1; break; 889 case '\\': codepoint = 0x5C; referenceOffset = i + 1; break; 890 } 891 892 if (codepoint == -1) { 893 894 if (c1 >= '0' && c1 <= '7') { 895 896 int f = i + 2; 897 while (f < (i + 4) && f < max) { 898 final char cf = unicodeEscapedText[f]; 899 if (!(cf >= '0' && cf <= '7')) { 900 break; 901 } 902 f++; 903 } 904 905 codepoint = parseIntFromReference(unicodeEscapedText, i + 1, f, 8); 906 907 if (codepoint > 0xFF) { 908 codepoint = parseIntFromReference(unicodeEscapedText, i + 1, f - 1, 8); 909 referenceOffset = f - 2; 910 } else { 911 referenceOffset = f - 1; 912 } 913 } else { 914 i++; 915 continue; 916 917 } 918 919 } 920 921 } 922 923 if (i - readOffset > 0) { 924 writer.write(unicodeEscapedText, readOffset, (i - readOffset)); 925 } 926 927 i = referenceOffset; 928 readOffset = i + 1; 929 930 if (codepoint > '\uFFFF') { 931 writer.write(Character.toChars(codepoint)); 932 } else { 933 writer.write((char)codepoint); 934 } 935 936 } 937 938 if (max - readOffset > 0) { 939 writer.write(unicodeEscapedText, readOffset, (max - readOffset)); 940 } 941 942 943 } 944 945 946 947 948 private static int codePointAt(final char c1, final char c2) { 949 if (Character.isHighSurrogate(c1)) { 950 if (c2 >= 0) { 951 if (Character.isLowSurrogate(c2)) { 952 return Character.toCodePoint(c1, c2); 953 } 954 } 955 } 956 return c1; 957 } 958 959 960 961}