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