1f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/*
2f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
3f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
4f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * you may not use this file except in compliance with the License.
6f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * You may obtain a copy of the License at
7f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
8f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
10f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * See the License for the specific language governing permissions and
14f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * limitations under the License.
15f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */
16f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
17f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectpackage com.android.dx.rop.cst;
18f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
19f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport com.android.dx.rop.type.Type;
20333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilsonimport com.android.dx.util.ByteArray;
21333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilsonimport com.android.dx.util.Hex;
22f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
23f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/**
24333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson * Constants of type {@code CONSTANT_Utf8_info} or {@code CONSTANT_String_info}.
25f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */
26333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilsonpublic final class CstString extends TypedConstant {
27333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
28333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * {@code non-null;} instance representing {@code ""}, that is, the
29333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * empty string
30333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
31333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public static final CstString EMPTY_STRING = new CstString("");
32333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
33333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /** {@code non-null;} the UTF-8 value as a string */
34333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    private final String string;
35333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
36333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /** {@code non-null;} the UTF-8 value as bytes */
37333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    private final ByteArray bytes;
38333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
39333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
40333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Converts a string into its MUTF-8 form. MUTF-8 differs from normal UTF-8
41333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * in the handling of character '\0' and surrogate pairs.
42333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     *
43333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @param string {@code non-null;} the string to convert
44333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return {@code non-null;} the UTF-8 bytes for it
45333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
46333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public static byte[] stringToUtf8Bytes(String string) {
47333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        int len = string.length();
48333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        byte[] bytes = new byte[len * 3]; // Avoid having to reallocate.
49333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        int outAt = 0;
50333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
51333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        for (int i = 0; i < len; i++) {
52333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            char c = string.charAt(i);
53333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            if ((c != 0) && (c < 0x80)) {
54333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                bytes[outAt] = (byte) c;
55333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                outAt++;
56333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            } else if (c < 0x800) {
57333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                bytes[outAt] = (byte) (((c >> 6) & 0x1f) | 0xc0);
58333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                bytes[outAt + 1] = (byte) ((c & 0x3f) | 0x80);
59333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                outAt += 2;
60333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            } else {
61333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                bytes[outAt] = (byte) (((c >> 12) & 0x0f) | 0xe0);
62333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                bytes[outAt + 1] = (byte) (((c >> 6) & 0x3f) | 0x80);
63333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                bytes[outAt + 2] = (byte) ((c & 0x3f) | 0x80);
64333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                outAt += 3;
65333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            }
66333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        }
67333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
68333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        byte[] result = new byte[outAt];
69333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        System.arraycopy(bytes, 0, result, 0, outAt);
70333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return result;
71333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
72f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
73f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
74333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Converts an array of UTF-8 bytes into a string.
75de75089fb7216d19e9c22cce4dc62a49513477d3Carl Shapiro     *
76333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @param bytes {@code non-null;} the bytes to convert
77333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return {@code non-null;} the converted string
78f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
79333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public static String utf8BytesToString(ByteArray bytes) {
80333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        int length = bytes.size();
81333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        char[] chars = new char[length]; // This is sized to avoid a realloc.
82333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        int outAt = 0;
83333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
84333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        for (int at = 0; length > 0; /*at*/) {
85333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            int v0 = bytes.getUnsignedByte(at);
86333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            char out;
87333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            switch (v0 >> 4) {
88333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                case 0x00: case 0x01: case 0x02: case 0x03:
89333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                case 0x04: case 0x05: case 0x06: case 0x07: {
90333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    // 0XXXXXXX -- single-byte encoding
91333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    length--;
92333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    if (v0 == 0) {
93333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        // A single zero byte is illegal.
94333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        return throwBadUtf8(v0, at);
95333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
96333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    out = (char) v0;
97333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    at++;
98333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    break;
99333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                }
100333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                case 0x0c: case 0x0d: {
101333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    // 110XXXXX -- two-byte encoding
102333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    length -= 2;
103333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    if (length < 0) {
104333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        return throwBadUtf8(v0, at);
105333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
106333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    int v1 = bytes.getUnsignedByte(at + 1);
107333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    if ((v1 & 0xc0) != 0x80) {
108333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        return throwBadUtf8(v1, at + 1);
109333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
110333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f);
111333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    if ((value != 0) && (value < 0x80)) {
112333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        /*
113333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         * This should have been represented with
114333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         * one-byte encoding.
115333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         */
116333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        return throwBadUtf8(v1, at + 1);
117333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
118333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    out = (char) value;
119333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    at += 2;
120333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    break;
121333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                }
122333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                case 0x0e: {
123333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    // 1110XXXX -- three-byte encoding
124333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    length -= 3;
125333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    if (length < 0) {
126333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        return throwBadUtf8(v0, at);
127333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
128333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    int v1 = bytes.getUnsignedByte(at + 1);
129333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    if ((v1 & 0xc0) != 0x80) {
130333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        return throwBadUtf8(v1, at + 1);
131333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
132333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    int v2 = bytes.getUnsignedByte(at + 2);
133333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    if ((v1 & 0xc0) != 0x80) {
134333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        return throwBadUtf8(v2, at + 2);
135333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
136333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) |
137333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        (v2 & 0x3f);
138333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    if (value < 0x800) {
139333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        /*
140333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         * This should have been represented with one- or
141333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         * two-byte encoding.
142333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         */
143333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        return throwBadUtf8(v2, at + 2);
144333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
145333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    out = (char) value;
146333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    at += 3;
147333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    break;
148333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                }
149333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                default: {
150333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    // 10XXXXXX, 1111XXXX -- illegal
151333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    return throwBadUtf8(v0, at);
152333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                }
153333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            }
154333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            chars[outAt] = out;
155333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            outAt++;
156333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        }
157333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
158333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return new String(chars, 0, outAt);
159333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
160333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
161333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
162333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Helper for {@link #utf8BytesToString}, which throws the right
163333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * exception for a bogus utf-8 byte.
164333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     *
165333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @param value the byte value
166333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @param offset the file offset
167333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return never
168333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @throws IllegalArgumentException always thrown
169333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
170333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    private static String throwBadUtf8(int value, int offset) {
171333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) +
172333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                                           " at offset " + Hex.u4(offset));
173333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
174333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
175333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
176333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Constructs an instance from a {@code String}.
177333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     *
178333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @param string {@code non-null;} the UTF-8 value as a string
179333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
180333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public CstString(String string) {
181f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (string == null) {
182f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            throw new NullPointerException("string == null");
183f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
184f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
185333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        this.string = string.intern();
186333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        this.bytes = new ByteArray(stringToUtf8Bytes(string));
187f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
188f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
189f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
190333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Constructs an instance from some UTF-8 bytes.
191de75089fb7216d19e9c22cce4dc62a49513477d3Carl Shapiro     *
192333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @param bytes {@code non-null;} array of the UTF-8 bytes
193f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
194333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public CstString(ByteArray bytes) {
195333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        if (bytes == null) {
196333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            throw new NullPointerException("bytes == null");
197333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        }
198333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
199333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        this.bytes = bytes;
200333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        this.string = utf8BytesToString(bytes).intern();
201f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
202f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
203f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
204f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
205f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public boolean equals(Object other) {
206f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (!(other instanceof CstString)) {
207f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return false;
208f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
209f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
210f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return string.equals(((CstString) other).string);
211f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
212f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
213f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
214f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
215f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public int hashCode() {
216f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return string.hashCode();
217f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
218f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
219f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
220f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
221f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    protected int compareTo0(Constant other) {
222f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return string.compareTo(((CstString) other).string);
223f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
224f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
225f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
226f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
227f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String toString() {
228333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return "string{\"" + toHuman() + "\"}";
229f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
230f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
231f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
232f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
233f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String typeName() {
234333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return "utf8";
235f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
236f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
237f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
238f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
239f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public boolean isCategory2() {
240f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return false;
241f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
242f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
243f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
244f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String toHuman() {
245333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        int len = string.length();
246333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        StringBuilder sb = new StringBuilder(len * 3 / 2);
247333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
248333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        for (int i = 0; i < len; i++) {
249333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            char c = string.charAt(i);
250333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            if ((c >= ' ') && (c < 0x7f)) {
251333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                if ((c == '\'') || (c == '\"') || (c == '\\')) {
252333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    sb.append('\\');
253333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                }
254333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                sb.append(c);
255333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            } else if (c <= 0x7f) {
256333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                switch (c) {
257333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    case '\n': sb.append("\\n"); break;
258333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    case '\r': sb.append("\\r"); break;
259333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    case '\t': sb.append("\\t"); break;
260333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    default: {
261333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        /*
262333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         * Represent the character as an octal escape.
263333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         * If the next character is a valid octal
264333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         * digit, disambiguate by using the
265333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         * three-digit form.
266333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                         */
267333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        char nextChar =
268333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                            (i < (len - 1)) ? string.charAt(i + 1) : 0;
269333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        boolean displayZero =
270333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                            (nextChar >= '0') && (nextChar <= '7');
271333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        sb.append('\\');
272333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        for (int shift = 6; shift >= 0; shift -= 3) {
273333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                            char outChar = (char) (((c >> shift) & 7) + '0');
274333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                            if ((outChar != '0') || displayZero) {
275333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                                sb.append(outChar);
276333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                                displayZero = true;
277333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                            }
278333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        }
279333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        if (! displayZero) {
280333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                            // Ironic edge case: The original value was 0.
281333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                            sb.append('0');
282333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        }
283333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                        break;
284333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                    }
285333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                }
286333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            } else {
287333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                sb.append("\\u");
288333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                sb.append(Character.forDigit(c >> 12, 16));
289333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                sb.append(Character.forDigit((c >> 8) & 0x0f, 16));
290333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                sb.append(Character.forDigit((c >> 4) & 0x0f, 16));
291333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson                sb.append(Character.forDigit(c & 0x0f, 16));
292333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            }
293333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        }
294333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
295333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return sb.toString();
296f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
297f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
298f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
299333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Gets the value as a human-oriented string, surrounded by double
300333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * quotes.
301de75089fb7216d19e9c22cce4dc62a49513477d3Carl Shapiro     *
302333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return {@code non-null;} the quoted string
303f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
304333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public String toQuoted() {
305333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return '\"' + toHuman() + '\"';
306333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
307333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
308333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
309333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Gets the value as a human-oriented string, surrounded by double
310333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * quotes, but ellipsizes the result if it is longer than the given
311333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * maximum length
312333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     *
313333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @param maxLength {@code >= 5;} the maximum length of the string to return
314333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return {@code non-null;} the quoted string
315333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
316333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public String toQuoted(int maxLength) {
317333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        String string = toHuman();
318333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        int length = string.length();
319333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        String ellipses;
320333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
321333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        if (length <= (maxLength - 2)) {
322333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            ellipses = "";
323333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        } else {
324333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            string = string.substring(0, maxLength - 5);
325333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson            ellipses = "...";
326333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        }
327333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
328333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return '\"' + string + ellipses + '\"';
329333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
330333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
331333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
332333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Gets the UTF-8 value as a string.
333333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * The returned string is always already interned.
334333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     *
335333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return {@code non-null;} the UTF-8 value as a string
336333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
337333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public String getString() {
338f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return string;
339f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
340333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
341333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
342333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Gets the UTF-8 value as UTF-8 encoded bytes.
343333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     *
344333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return {@code non-null;} an array of the UTF-8 bytes
345333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
346333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public ByteArray getBytes() {
347333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return bytes;
348333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
349333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
350333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
351333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Gets the size of this instance as UTF-8 code points. That is,
352333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * get the number of bytes in the UTF-8 encoding of this instance.
353333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     *
354333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return {@code >= 0;} the UTF-8 size
355333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
356333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public int getUtf8Size() {
357333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return bytes.size();
358333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
359333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
360333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    /**
361333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Gets the size of this instance as UTF-16 code points. That is,
362333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * get the number of 16-bit chars in the UTF-16 encoding of this
363333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * instance. This is the same as the {@code length} of the
364333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * Java {@code String} representation of this instance.
365333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     *
366333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     * @return {@code >= 0;} the UTF-16 size
367333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson     */
368333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public int getUtf16Size() {
369333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return string.length();
370333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
371333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson
372333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    public Type getType() {
373333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson        return Type.STRING;
374333201833d506a3accdeac6ceb7caba8d4b95797Jesse Wilson    }
375f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project}
376