1/*
2 * Copyright (C) 2013 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.support.v4.text;
18
19import android.support.annotation.NonNull;
20import android.support.annotation.Nullable;
21import android.support.v4.view.ViewCompat;
22
23import java.util.Locale;
24
25public class TextUtilsCompat {
26
27    /**
28     * Html-encode the string.
29     * @param s the string to be encoded
30     * @return the encoded string
31     */
32    @NonNull
33    public static String htmlEncode(@NonNull String s) {
34        StringBuilder sb = new StringBuilder();
35        char c;
36        for (int i = 0; i < s.length(); i++) {
37            c = s.charAt(i);
38            switch (c) {
39                case '<':
40                    sb.append("&lt;"); //$NON-NLS-1$
41                    break;
42                case '>':
43                    sb.append("&gt;"); //$NON-NLS-1$
44                    break;
45                case '&':
46                    sb.append("&amp;"); //$NON-NLS-1$
47                    break;
48                case '\'':
49                    //http://www.w3.org/TR/xhtml1
50                    // The named character reference &apos; (the apostrophe, U+0027) was introduced in
51                    // XML 1.0 but does not appear in HTML. Authors should therefore use &#39; instead
52                    // of &apos; to work as expected in HTML 4 user agents.
53                    sb.append("&#39;"); //$NON-NLS-1$
54                    break;
55                case '"':
56                    sb.append("&quot;"); //$NON-NLS-1$
57                    break;
58                default:
59                    sb.append(c);
60            }
61        }
62        return sb.toString();
63    }
64
65    /**
66     * Return the layout direction for a given Locale
67     *
68     * @param locale the Locale for which we want the layout direction. Can be null.
69     * @return the layout direction. This may be one of:
70     * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
71     * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
72     *
73     * Be careful: this code will need to be updated when vertical scripts will be supported
74     */
75    public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
76        if (locale != null && !locale.equals(ROOT)) {
77            final String scriptSubtag = ICUCompat.getScript(
78                    ICUCompat.addLikelySubtags(locale.toString()));
79            if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale);
80
81            if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
82                    scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
83                return ViewCompat.LAYOUT_DIRECTION_RTL;
84            }
85        }
86
87        return ViewCompat.LAYOUT_DIRECTION_LTR;
88    }
89
90    /**
91     * Fallback algorithm to detect the locale direction. Rely on the fist char of the
92     * localized locale name. This will not work if the localized locale name is in English
93     * (this is the case for ICU 4.4 and "Urdu" script)
94     *
95     * @param locale
96     * @return the layout direction. This may be one of:
97     * {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
98     * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
99     *
100     * Be careful: this code will need to be updated when vertical scripts will be supported
101     */
102    private static int getLayoutDirectionFromFirstChar(Locale locale) {
103        switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
104            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
105            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
106                return ViewCompat.LAYOUT_DIRECTION_RTL;
107
108            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
109            default:
110                return ViewCompat.LAYOUT_DIRECTION_LTR;
111        }
112    }
113
114    public static final Locale ROOT = new Locale("", "");
115
116    private static String ARAB_SCRIPT_SUBTAG = "Arab";
117    private static String HEBR_SCRIPT_SUBTAG = "Hebr";
118}
119