1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.util;
18
19import android.os.Build;
20import android.text.TextUtils;
21
22import java.nio.charset.Charset;
23import java.nio.charset.IllegalCharsetNameException;
24import java.nio.charset.UnsupportedCharsetException;
25import java.util.HashMap;
26import java.util.Map;
27
28/**
29 * <p>
30 * A class containing utility methods related to character sets. This
31 * class is primarily useful for code that wishes to be vendor-aware
32 * in its interpretation of Japanese charset names (used in DoCoMo,
33 * KDDI, and SoftBank).
34 * </p>
35 *
36 * <p>
37 * <b>Note:</b> Developers will need to add an appropriate mapping for
38 * each vendor-specific charset. You may need to modify the C libraries
39 * like icu4c in order to let Android support an additional charset.
40 * </p>
41 *
42 * @hide
43 */
44public final class CharsetUtils {
45    /**
46     * name of the vendor "DoCoMo". <b>Note:</b> This isn't a public
47     * constant, in order to keep this class from becoming a de facto
48     * reference list of vendor names.
49     */
50    private static final String VENDOR_DOCOMO = "docomo";
51    /**
52     * Name of the vendor "KDDI".
53     */
54    private static final String VENDOR_KDDI = "kddi";
55    /**
56     * Name of the vendor "SoftBank".
57     */
58    private static final String VENDOR_SOFTBANK = "softbank";
59
60    /**
61     * Represents one-to-one mapping from a vendor name to a charset specific to the vendor.
62     */
63    private static final Map<String, String> sVendorShiftJisMap = new HashMap<String, String>();
64
65    static {
66        // These variants of Shift_JIS come from icu's mapping data (convrtrs.txt)
67        sVendorShiftJisMap.put(VENDOR_DOCOMO, "docomo-shift_jis-2007");
68        sVendorShiftJisMap.put(VENDOR_KDDI, "kddi-shift_jis-2007");
69        sVendorShiftJisMap.put(VENDOR_SOFTBANK, "softbank-shift_jis-2007");
70    }
71
72    /**
73     * This class is uninstantiable.
74     */
75    private CharsetUtils() {
76        // This space intentionally left blank.
77    }
78
79    /**
80     * Returns the name of the vendor-specific character set
81     * corresponding to the given original character set name and
82     * vendor. If there is no vendor-specific character set for the
83     * given name/vendor pair, this returns the original character set name.
84     *
85     * @param charsetName the base character set name
86     * @param vendor the vendor to specialize for. All characters should be lower-cased.
87     * @return the specialized character set name, or {@code charsetName} if
88     * there is no specialized name
89     */
90    public static String nameForVendor(String charsetName, String vendor) {
91        if (!TextUtils.isEmpty(charsetName) && !TextUtils.isEmpty(vendor)) {
92            // You can add your own mapping here.
93            if (isShiftJis(charsetName)) {
94                final String vendorShiftJis = sVendorShiftJisMap.get(vendor);
95                if (vendorShiftJis != null) {
96                    return vendorShiftJis;
97                }
98            }
99        }
100
101        return charsetName;
102    }
103
104    /**
105     * Returns the name of the vendor-specific character set
106     * corresponding to the given original character set name and the
107     * default vendor (that is, the targeted vendor of the device this
108     * code is running on). This method merely calls through to
109     * {@link #nameForVendor(String,String)}, passing the default vendor
110     * as the second argument.
111     *
112     * @param charsetName the base character set name
113     * @return the specialized character set name, or {@code charsetName} if
114     * there is no specialized name
115     */
116    public static String nameForDefaultVendor(String charsetName) {
117        return nameForVendor(charsetName, getDefaultVendor());
118    }
119
120    /**
121     * Returns the vendor-specific character set corresponding to the
122     * given original character set name and vendor. If there is no
123     * vendor-specific character set for the given name/vendor pair,
124     * this returns the character set corresponding to the original
125     * name. The vendor name is matched case-insensitively. This
126     * method merely calls {@code Charset.forName()} on a name
127     * transformed by a call to {@link #nameForVendor(String,String)}.
128     *
129     * @param charsetName the base character set name
130     * @param vendor the vendor to specialize for
131     * @return the specialized character set, or the one corresponding
132     * directly to {@code charsetName} if there is no specialized
133     * variant
134     * @throws UnsupportedCharsetException thrown if the named character
135     * set is not supported by the system
136     * @throws IllegalCharsetNameException thrown if {@code charsetName}
137     * has invalid syntax
138     */
139    public static Charset charsetForVendor(String charsetName, String vendor)
140            throws UnsupportedCharsetException, IllegalCharsetNameException {
141        charsetName = nameForVendor(charsetName, vendor);
142        return Charset.forName(charsetName);
143    }
144
145    /**
146     * Returns the vendor-specific character set corresponding to the
147     * given original character set name and default vendor (that is,
148     * the targeted vendor of the device this code is running on).
149     * This method merely calls through to {@link
150     * #charsetForVendor(String,String)}, passing the default vendor
151     * as the second argument.
152     *
153     * @param charsetName the base character set name
154     * @return the specialized character set, or the one corresponding
155     * directly to {@code charsetName} if there is no specialized
156     * variant
157     * @throws UnsupportedCharsetException thrown if the named character
158     * set is not supported by the system
159     * @throws IllegalCharsetNameException thrown if {@code charsetName}
160     * has invalid syntax
161     */
162    public static Charset charsetForVendor(String charsetName)
163            throws UnsupportedCharsetException, IllegalCharsetNameException {
164        return charsetForVendor(charsetName, getDefaultVendor());
165    }
166
167    /**
168     * Returns whether the given character set name indicates the Shift-JIS
169     * encoding. Returns false if the name is null.
170     *
171     * @param charsetName the character set name
172     * @return {@code true} if the name corresponds to Shift-JIS or
173     * {@code false} if not
174     */
175    private static boolean isShiftJis(String charsetName) {
176        // Bail quickly if the length doesn't match.
177        if (charsetName == null) {
178            return false;
179        }
180        int length = charsetName.length();
181        if (length != 4 && length != 9) {
182            return false;
183        }
184
185        return charsetName.equalsIgnoreCase("shift_jis")
186            || charsetName.equalsIgnoreCase("shift-jis")
187            || charsetName.equalsIgnoreCase("sjis");
188    }
189
190    /**
191     * Gets the default vendor for this build.
192     *
193     * @return the default vendor name
194     */
195    private static String getDefaultVendor() {
196        return Build.BRAND;
197    }
198}
199