Path: blob/master/src/java.base/share/classes/jdk/internal/icu/impl/Utility.java
41161 views
/*1* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24/*25*******************************************************************************26* Copyright (C) 1996-2011, International Business Machines Corporation and *27* others. All Rights Reserved. *28*******************************************************************************29*/3031package jdk.internal.icu.impl;3233import jdk.internal.icu.lang.UCharacter;34import jdk.internal.icu.text.UTF16;3536import java.io.IOException;37import java.util.Locale;3839public final class Utility {4041/**42* Convert characters outside the range U+0020 to U+007F to43* Unicode escapes, and convert backslash to a double backslash.44*/45public static final String escape(String s) {46StringBuilder buf = new StringBuilder();47for (int i=0; i<s.length(); ) {48int c = Character.codePointAt(s, i);49i += UTF16.getCharCount(c);50if (c >= ' ' && c <= 0x007F) {51if (c == '\\') {52buf.append("\\\\"); // That is, "\\"53} else {54buf.append((char)c);55}56} else {57boolean four = c <= 0xFFFF;58buf.append(four ? "\\u" : "\\U");59buf.append(hex(c, four ? 4 : 8));60}61}62return buf.toString();63}6465/* This map must be in ASCENDING ORDER OF THE ESCAPE CODE */66private static final char[] UNESCAPE_MAP = {67/*" 0x22, 0x22 */68/*' 0x27, 0x27 */69/*? 0x3F, 0x3F */70/*\ 0x5C, 0x5C */71/*a*/ 0x61, 0x07,72/*b*/ 0x62, 0x08,73/*e*/ 0x65, 0x1b,74/*f*/ 0x66, 0x0c,75/*n*/ 0x6E, 0x0a,76/*r*/ 0x72, 0x0d,77/*t*/ 0x74, 0x09,78/*v*/ 0x76, 0x0b79};8081/**82* Convert an escape to a 32-bit code point value. We attempt83* to parallel the icu4c unescapeAt() function.84* @param offset16 an array containing offset to the character85* <em>after</em> the backslash. Upon return offset16[0] will86* be updated to point after the escape sequence.87* @return character value from 0 to 10FFFF, or -1 on error.88*/89public static int unescapeAt(String s, int[] offset16) {90int c;91int result = 0;92int n = 0;93int minDig = 0;94int maxDig = 0;95int bitsPerDigit = 4;96int dig;97int i;98boolean braces = false;99100/* Check that offset is in range */101int offset = offset16[0];102int length = s.length();103if (offset < 0 || offset >= length) {104return -1;105}106107/* Fetch first UChar after '\\' */108c = Character.codePointAt(s, offset);109offset += UTF16.getCharCount(c);110111/* Convert hexadecimal and octal escapes */112switch (c) {113case 'u':114minDig = maxDig = 4;115break;116case 'U':117minDig = maxDig = 8;118break;119case 'x':120minDig = 1;121if (offset < length && UTF16.charAt(s, offset) == 0x7B /*{*/) {122++offset;123braces = true;124maxDig = 8;125} else {126maxDig = 2;127}128break;129default:130dig = UCharacter.digit(c, 8);131if (dig >= 0) {132minDig = 1;133maxDig = 3;134n = 1; /* Already have first octal digit */135bitsPerDigit = 3;136result = dig;137}138break;139}140if (minDig != 0) {141while (offset < length && n < maxDig) {142c = UTF16.charAt(s, offset);143dig = UCharacter.digit(c, (bitsPerDigit == 3) ? 8 : 16);144if (dig < 0) {145break;146}147result = (result << bitsPerDigit) | dig;148offset += UTF16.getCharCount(c);149++n;150}151if (n < minDig) {152return -1;153}154if (braces) {155if (c != 0x7D /*}*/) {156return -1;157}158++offset;159}160if (result < 0 || result >= 0x110000) {161return -1;162}163// If an escape sequence specifies a lead surrogate, see164// if there is a trail surrogate after it, either as an165// escape or as a literal. If so, join them up into a166// supplementary.167if (offset < length &&168UTF16.isLeadSurrogate((char) result)) {169int ahead = offset+1;170c = s.charAt(offset); // [sic] get 16-bit code unit171if (c == '\\' && ahead < length) {172int o[] = new int[] { ahead };173c = unescapeAt(s, o);174ahead = o[0];175}176if (UTF16.isTrailSurrogate((char) c)) {177offset = ahead;178result = UCharacterProperty.getRawSupplementary(179(char) result, (char) c);180}181}182offset16[0] = offset;183return result;184}185186/* Convert C-style escapes in table */187for (i=0; i<UNESCAPE_MAP.length; i+=2) {188if (c == UNESCAPE_MAP[i]) {189offset16[0] = offset;190return UNESCAPE_MAP[i+1];191} else if (c < UNESCAPE_MAP[i]) {192break;193}194}195196/* Map \cX to control-X: X & 0x1F */197if (c == 'c' && offset < length) {198c = UTF16.charAt(s, offset);199offset16[0] = offset + UTF16.getCharCount(c);200return 0x1F & c;201}202203/* If no special forms are recognized, then consider204* the backslash to generically escape the next character. */205offset16[0] = offset;206return c;207}208209/**210* Supplies a zero-padded hex representation of an integer (without 0x)211*/212public static String hex(long i, int places) {213if (i == Long.MIN_VALUE) return "-8000000000000000";214boolean negative = i < 0;215if (negative) {216i = -i;217}218String result = Long.toString(i, 16).toUpperCase(Locale.ENGLISH);219if (result.length() < places) {220result = "0000000000000000".substring(result.length(),places) + result;221}222if (negative) {223return '-' + result;224}225return result;226}227228static final char DIGITS[] = {229'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',230'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',231'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',232'U', 'V', 'W', 'X', 'Y', 'Z'233};234235/**236* Return true if the character is NOT printable ASCII. The tab,237* newline and linefeed characters are considered unprintable.238*/239public static boolean isUnprintable(int c) {240//0x20 = 32 and 0x7E = 126241return !(c >= 0x20 && c <= 0x7E);242}243244/**245* Escape unprintable characters using <backslash>uxxxx notation246* for U+0000 to U+FFFF and <backslash>Uxxxxxxxx for U+10000 and247* above. If the character is printable ASCII, then do nothing248* and return FALSE. Otherwise, append the escaped notation and249* return TRUE.250*/251public static <T extends Appendable> boolean escapeUnprintable(T result, int c) {252try {253if (isUnprintable(c)) {254result.append('\\');255if ((c & ~0xFFFF) != 0) {256result.append('U');257result.append(DIGITS[0xF&(c>>28)]);258result.append(DIGITS[0xF&(c>>24)]);259result.append(DIGITS[0xF&(c>>20)]);260result.append(DIGITS[0xF&(c>>16)]);261} else {262result.append('u');263}264result.append(DIGITS[0xF&(c>>12)]);265result.append(DIGITS[0xF&(c>>8)]);266result.append(DIGITS[0xF&(c>>4)]);267result.append(DIGITS[0xF&c]);268return true;269}270return false;271} catch (IOException e) {272throw new IllegalArgumentException(e);273}274}275}276277278