1917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul/* 2917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Copyright (C) 2007 The Android Open Source Project 3917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 4917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Licensed under the Apache License, Version 2.0 (the "License"); 5917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * you may not use this file except in compliance with the License. 6917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * You may obtain a copy of the License at 7917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 8917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * http://www.apache.org/licenses/LICENSE-2.0 9917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 10917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Unless required by applicable law or agreed to in writing, software 11917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * distributed under the License is distributed on an "AS IS" BASIS, 12917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * See the License for the specific language governing permissions and 14917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * limitations under the License. 15917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 16917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 17917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulpackage com.android.dexgen.rop.cst; 18917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 19917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport com.android.dexgen.util.ByteArray; 20917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport com.android.dexgen.util.Hex; 21917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 22917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul/** 23917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Constants of type {@code CONSTANT_Utf8_info}. 24917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 25917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulpublic final class CstUtf8 extends Constant { 26917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 27917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * {@code non-null;} instance representing {@code ""}, that is, the 28917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * empty string 29917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 30917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public static final CstUtf8 EMPTY_STRING = new CstUtf8(""); 31917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 32917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@code non-null;} the UTF-8 value as a string */ 33917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private final String string; 34917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 35917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@code non-null;} the UTF-8 value as bytes */ 36917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private final ByteArray bytes; 37917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 38917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 39917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Converts a string into its Java-style UTF-8 form. Java-style UTF-8 40917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * differs from normal UTF-8 in the handling of character '\0' and 41917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * surrogate pairs. 42917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 43917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param string {@code non-null;} the string to convert 44917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} the UTF-8 bytes for it 45917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 46917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public static byte[] stringToUtf8Bytes(String string) { 47917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int len = string.length(); 48917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul byte[] bytes = new byte[len * 3]; // Avoid having to reallocate. 49917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int outAt = 0; 50917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 51917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul for (int i = 0; i < len; i++) { 52917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul char c = string.charAt(i); 53917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((c != 0) && (c < 0x80)) { 54917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul bytes[outAt] = (byte) c; 55917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul outAt++; 56917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (c < 0x800) { 57917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul bytes[outAt] = (byte) (((c >> 6) & 0x1f) | 0xc0); 58917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul bytes[outAt + 1] = (byte) ((c & 0x3f) | 0x80); 59917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul outAt += 2; 60917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else { 61917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul bytes[outAt] = (byte) (((c >> 12) & 0x0f) | 0xe0); 62917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul bytes[outAt + 1] = (byte) (((c >> 6) & 0x3f) | 0x80); 63917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul bytes[outAt + 2] = (byte) ((c & 0x3f) | 0x80); 64917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul outAt += 3; 65917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 66917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 67917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 68917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul byte[] result = new byte[outAt]; 69917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul System.arraycopy(bytes, 0, result, 0, outAt); 70917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return result; 71917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 72917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 73917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 74917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Converts an array of UTF-8 bytes into a string. 75917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 76917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param bytes {@code non-null;} the bytes to convert 77917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} the converted string 78917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 79917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public static String utf8BytesToString(ByteArray bytes) { 80917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int length = bytes.size(); 81917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul char[] chars = new char[length]; // This is sized to avoid a realloc. 82917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int outAt = 0; 83917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 84917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul for (int at = 0; length > 0; /*at*/) { 85917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int v0 = bytes.getUnsignedByte(at); 86917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul char out; 87917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul switch (v0 >> 4) { 88917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul case 0x00: case 0x01: case 0x02: case 0x03: 89917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul case 0x04: case 0x05: case 0x06: case 0x07: { 90917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // 0XXXXXXX -- single-byte encoding 91917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul length--; 92917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (v0 == 0) { 93917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // A single zero byte is illegal. 94917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v0, at); 95917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 96917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul out = (char) v0; 97917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul at++; 98917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul break; 99917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 100917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul case 0x0c: case 0x0d: { 101917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // 110XXXXX -- two-byte encoding 102917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul length -= 2; 103917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (length < 0) { 104917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v0, at); 105917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 106917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int v1 = bytes.getUnsignedByte(at + 1); 107917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((v1 & 0xc0) != 0x80) { 108917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v1, at + 1); 109917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 110917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f); 111917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((value != 0) && (value < 0x80)) { 112917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /* 113917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * This should have been represented with 114917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * one-byte encoding. 115917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 116917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v1, at + 1); 117917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 118917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul out = (char) value; 119917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul at += 2; 120917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul break; 121917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 122917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul case 0x0e: { 123917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // 1110XXXX -- three-byte encoding 124917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul length -= 3; 125917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (length < 0) { 126917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v0, at); 127917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 128917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int v1 = bytes.getUnsignedByte(at + 1); 129917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((v1 & 0xc0) != 0x80) { 130917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v1, at + 1); 131917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 132917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int v2 = bytes.getUnsignedByte(at + 2); 133917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((v1 & 0xc0) != 0x80) { 134917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v2, at + 2); 135917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 136917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) | 137917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul (v2 & 0x3f); 138917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (value < 0x800) { 139917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /* 140917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * This should have been represented with one- or 141917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * two-byte encoding. 142917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 143917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v2, at + 2); 144917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 145917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul out = (char) value; 146917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul at += 3; 147917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul break; 148917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 149917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul default: { 150917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // 10XXXXXX, 1111XXXX -- illegal 151917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return throwBadUtf8(v0, at); 152917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 153917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 154917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul chars[outAt] = out; 155917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul outAt++; 156917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 157917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 158917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return new String(chars, 0, outAt); 159917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 160917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 161917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 162917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Helper for {@link #utf8BytesToString}, which throws the right 163917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * exception for a bogus utf-8 byte. 164917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 165917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param value the byte value 166917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param offset the file offset 167917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return never 168917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @throws IllegalArgumentException always thrown 169917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 170917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul private static String throwBadUtf8(int value, int offset) { 171917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) + 172917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul " at offset " + Hex.u4(offset)); 173917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 174917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 175917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 176917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Constructs an instance from a {@code String}. 177917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 178917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param string {@code non-null;} the UTF-8 value as a string 179917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 180917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public CstUtf8(String string) { 181917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (string == null) { 182917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new NullPointerException("string == null"); 183917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 184917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 185917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.string = string.intern(); 186917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.bytes = new ByteArray(stringToUtf8Bytes(string)); 187917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 188917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 189917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 190917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Constructs an instance from some UTF-8 bytes. 191917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 192917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param bytes {@code non-null;} array of the UTF-8 bytes 193917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 194917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public CstUtf8(ByteArray bytes) { 195917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (bytes == null) { 196917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul throw new NullPointerException("bytes == null"); 197917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 198917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 199917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.bytes = bytes; 200917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul this.string = utf8BytesToString(bytes).intern(); 201917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 202917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 203917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 204917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul @Override 205917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public boolean equals(Object other) { 206917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (!(other instanceof CstUtf8)) { 207917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return false; 208917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 209917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 210917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return string.equals(((CstUtf8) other).string); 211917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 212917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 213917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 214917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul @Override 215917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int hashCode() { 216917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return string.hashCode(); 217917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 218917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 219917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 220917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul @Override 221917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul protected int compareTo0(Constant other) { 222917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return string.compareTo(((CstUtf8) other).string); 223917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 224917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 225917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 226917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul @Override 227917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public String toString() { 228917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return "utf8{\"" + toHuman() + "\"}"; 229917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 230917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 231917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 232917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul @Override 233917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public String typeName() { 234917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return "utf8"; 235917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 236917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 237917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 238917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul @Override 239917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public boolean isCategory2() { 240917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return false; 241917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 242917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 243917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** {@inheritDoc} */ 244917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public String toHuman() { 245917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int len = string.length(); 246917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul StringBuilder sb = new StringBuilder(len * 3 / 2); 247917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 248917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul for (int i = 0; i < len; i++) { 249917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul char c = string.charAt(i); 250917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((c >= ' ') && (c < 0x7f)) { 251917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((c == '\'') || (c == '\"') || (c == '\\')) { 252917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append('\\'); 253917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 254917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append(c); 255917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else if (c <= 0x7f) { 256917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul switch (c) { 257917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul case '\n': sb.append("\\n"); break; 258917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul case '\r': sb.append("\\r"); break; 259917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul case '\t': sb.append("\\t"); break; 260917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul default: { 261917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /* 262917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Represent the character as an octal escape. 263917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * If the next character is a valid octal 264917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * digit, disambiguate by using the 265917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * three-digit form. 266917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 267917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul char nextChar = 268917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul (i < (len - 1)) ? string.charAt(i + 1) : 0; 269917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul boolean displayZero = 270917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul (nextChar >= '0') && (nextChar <= '7'); 271917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append('\\'); 272917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul for (int shift = 6; shift >= 0; shift -= 3) { 273917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul char outChar = (char) (((c >> shift) & 7) + '0'); 274917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if ((outChar != '0') || displayZero) { 275917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append(outChar); 276917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul displayZero = true; 277917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 278917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 279917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (! displayZero) { 280917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul // Ironic edge case: The original value was 0. 281917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append('0'); 282917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 283917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul break; 284917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 285917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 286917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else { 287917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append("\\u"); 288917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append(Character.forDigit(c >> 12, 16)); 289917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append(Character.forDigit((c >> 8) & 0x0f, 16)); 290917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append(Character.forDigit((c >> 4) & 0x0f, 16)); 291917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul sb.append(Character.forDigit(c & 0x0f, 16)); 292917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 293917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 294917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 295917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return sb.toString(); 296917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 297917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 298917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 299917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the value as a human-oriented string, surrounded by double 300917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * quotes. 301917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 302917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} the quoted string 303917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 304917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public String toQuoted() { 305917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return '\"' + toHuman() + '\"'; 306917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 307917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 308917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 309917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the value as a human-oriented string, surrounded by double 310917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * quotes, but ellipsizes the result if it is longer than the given 311917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * maximum length 312917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 313917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @param maxLength {@code >= 5;} the maximum length of the string to return 314917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} the quoted string 315917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 316917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public String toQuoted(int maxLength) { 317917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul String string = toHuman(); 318917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul int length = string.length(); 319917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul String ellipses; 320917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 321917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul if (length <= (maxLength - 2)) { 322917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ellipses = ""; 323917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } else { 324917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul string = string.substring(0, maxLength - 5); 325917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul ellipses = "..."; 326917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 327917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 328917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return '\"' + string + ellipses + '\"'; 329917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 330917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 331917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 332917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the UTF-8 value as a string. 333917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * The returned string is always already interned. 334917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 335917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} the UTF-8 value as a string 336917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 337917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public String getString() { 338917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return string; 339917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 340917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 341917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 342917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the UTF-8 value as UTF-8 encoded bytes. 343917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 344917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code non-null;} an array of the UTF-8 bytes 345917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 346917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public ByteArray getBytes() { 347917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return bytes; 348917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 349917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 350917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 351917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the size of this instance as UTF-8 code points. That is, 352917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * get the number of bytes in the UTF-8 encoding of this instance. 353917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 354917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code >= 0;} the UTF-8 size 355917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 356917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int getUtf8Size() { 357917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return bytes.size(); 358917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 359917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul 360917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul /** 361917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Gets the size of this instance as UTF-16 code points. That is, 362917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * get the number of 16-bit chars in the UTF-16 encoding of this 363917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * instance. This is the same as the {@code length} of the 364917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Java {@code String} representation of this instance. 365917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * 366917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * @return {@code >= 0;} the UTF-16 size 367917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */ 368917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul public int getUtf16Size() { 369917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul return string.length(); 370917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul } 371917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul} 372