19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.internal.R;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.ColorStateList;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcel;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcelable;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.method.TextKeyListener.Capitalize;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.AbsoluteSizeSpan;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.AlignmentSpan;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.BackgroundColorSpan;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.BulletSpan;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.CharacterStyle;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ForegroundColorSpan;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.LeadingMarginSpan;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.MetricAffectingSpan;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.QuoteSpan;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.RelativeSizeSpan;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ReplacementSpan;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ScaleXSpan;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.StrikethroughSpan;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.StyleSpan;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.SubscriptSpan;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.SuperscriptSpan;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.TextAppearanceSpan;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.TypefaceSpan;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.URLSpan;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.UnderlineSpan;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Printer;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.internal.util.ArrayUtils;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Iterator;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class TextUtils {
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private TextUtils() { /* cannot be instantiated */ }
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String[] EMPTY_STRING_ARRAY = new String[]{};
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void getChars(CharSequence s, int start, int end,
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                char[] dest, int destoff) {
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class c = s.getClass();
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c == String.class)
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((String) s).getChars(start, end, dest, destoff);
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (c == StringBuffer.class)
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((StringBuffer) s).getChars(start, end, dest, destoff);
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (c == StringBuilder.class)
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((StringBuilder) s).getChars(start, end, dest, destoff);
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else if (s instanceof GetChars)
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ((GetChars) s).getChars(start, end, dest, destoff);
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else {
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = start; i < end; i++)
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest[destoff++] = s.charAt(i);
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, char ch) {
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return indexOf(s, ch, 0);
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, char ch, int start) {
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class c = s.getClass();
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c == String.class)
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((String) s).indexOf(ch, start);
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return indexOf(s, ch, start, s.length());
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, char ch, int start, int end) {
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class c = s.getClass();
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (s instanceof GetChars || c == StringBuffer.class ||
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c == StringBuilder.class || c == String.class) {
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int INDEX_INCREMENT = 500;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] temp = obtain(INDEX_INCREMENT);
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (start < end) {
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int segend = start + INDEX_INCREMENT;
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (segend > end)
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    segend = end;
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                getChars(s, start, segend, temp, 0);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int count = segend - start;
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < count; i++) {
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (temp[i] == ch) {
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        recycle(temp);
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return i + start;
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                start = segend;
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recycle(temp);
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return -1;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = start; i < end; i++)
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (s.charAt(i) == ch)
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int lastIndexOf(CharSequence s, char ch) {
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return lastIndexOf(s, ch, s.length() - 1);
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int lastIndexOf(CharSequence s, char ch, int last) {
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class c = s.getClass();
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c == String.class)
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((String) s).lastIndexOf(ch, last);
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return lastIndexOf(s, ch, 0, last);
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int lastIndexOf(CharSequence s, char ch,
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                  int start, int last) {
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (last < 0)
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return -1;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (last >= s.length())
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            last = s.length() - 1;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end = last + 1;
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class c = s.getClass();
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (s instanceof GetChars || c == StringBuffer.class ||
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c == StringBuilder.class || c == String.class) {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int INDEX_INCREMENT = 500;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] temp = obtain(INDEX_INCREMENT);
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (start < end) {
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int segstart = end - INDEX_INCREMENT;
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (segstart < start)
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    segstart = start;
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                getChars(s, segstart, end, temp, 0);
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int count = end - segstart;
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = count - 1; i >= 0; i--) {
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (temp[i] == ch) {
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        recycle(temp);
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return i + segstart;
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end = segstart;
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            recycle(temp);
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return -1;
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = end - 1; i >= start; i--)
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (s.charAt(i) == ch)
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, CharSequence needle) {
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return indexOf(s, needle, 0, s.length());
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, CharSequence needle, int start) {
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return indexOf(s, needle, start, s.length());
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int indexOf(CharSequence s, CharSequence needle,
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                              int start, int end) {
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int nlen = needle.length();
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (nlen == 0)
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return start;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = needle.charAt(0);
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (;;) {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start = indexOf(s, c, start);
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start > end - nlen) {
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start < 0) {
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return -1;
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (regionMatches(s, start, needle, 0, nlen)) {
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return start;
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start++;
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean regionMatches(CharSequence one, int toffset,
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        CharSequence two, int ooffset,
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        int len) {
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] temp = obtain(2 * len);
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        getChars(one, toffset, toffset + len, temp, 0);
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        getChars(two, ooffset, ooffset + len, temp, len);
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean match = true;
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < len; i++) {
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (temp[i] != temp[i + len]) {
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                match = false;
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recycle(temp);
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return match;
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create a new String object containing the given range of characters
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * from the source string.  This is different than simply calling
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link CharSequence#subSequence(int, int) CharSequence.subSequence}
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in that it does not preserve any style runs in the source sequence,
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * allowing a more efficient implementation.
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String substring(CharSequence source, int start, int end) {
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof String)
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((String) source).substring(start, end);
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof StringBuilder)
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((StringBuilder) source).substring(start, end);
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof StringBuffer)
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return ((StringBuffer) source).substring(start, end);
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] temp = obtain(end - start);
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        getChars(source, start, end, temp, 0);
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String ret = new String(temp, 0, end - start);
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recycle(temp);
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a string containing the tokens joined by delimiters.
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tokens an array objects to be joined. Strings will be formed from
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *     the objects by calling object.toString().
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String join(CharSequence delimiter, Object[] tokens) {
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean firstTime = true;
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Object token: tokens) {
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (firstTime) {
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                firstTime = false;
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(delimiter);
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(token);
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a string containing the tokens joined by delimiters.
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tokens an array objects to be joined. Strings will be formed from
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *     the objects by calling object.toString().
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String join(CharSequence delimiter, Iterable tokens) {
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean firstTime = true;
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Object token: tokens) {
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (firstTime) {
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                firstTime = false;
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(delimiter);
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(token);
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * String.split() returns [''] when the string to be split is empty. This returns []. This does
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * not remove any empty strings from the result. For example split("a,", ","  ) returns {"a", ""}.
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text the string to split
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param expression the regular expression to match
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return an array of strings. The array will be empty if text is empty
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if expression or text is null
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String[] split(String text, String expression) {
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text.length() == 0) {
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return EMPTY_STRING_ARRAY;
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return text.split(expression, -1);
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Splits a string on a pattern. String.split() returns [''] when the string to be
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * split is empty. This returns []. This does not remove any empty strings from the result.
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text the string to split
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param pattern the regular expression to match
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return an array of strings. The array will be empty if text is empty
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws NullPointerException if expression or text is null
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String[] split(String text, Pattern pattern) {
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text.length() == 0) {
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return EMPTY_STRING_ARRAY;
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return pattern.split(text, -1);
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * An interface for splitting strings according to rules that are opaque to the user of this
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * interface. This also has less overhead than split, which uses regular expressions and
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * allocates an array to hold the results.
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>The most efficient way to use this class is:
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <pre>
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * // Once
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(delimiter);
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * // Once per string to split
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * splitter.setString(string);
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * for (String s : splitter) {
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *     ...
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * }
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * </pre>
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface StringSplitter extends Iterable<String> {
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setString(String string);
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * A simple string splitter.
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>If the final character in the string to split is the delimiter then no empty string will
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be returned for the empty string after that delimeter. That is, splitting <tt>"a,b,"</tt> on
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * comma will return <tt>"a", "b"</tt>, not <tt>"a", "b", ""</tt>.
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class SimpleStringSplitter implements StringSplitter, Iterator<String> {
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mString;
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private char mDelimiter;
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mPosition;
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mLength;
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Initializes the splitter. setString may be called later.
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param delimiter the delimeter on which to split
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public SimpleStringSplitter(char delimiter) {
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDelimiter = delimiter;
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the string to split
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param string the string to split
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setString(String string) {
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mString = string;
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPosition = 0;
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLength = mString.length();
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Iterator<String> iterator() {
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return this;
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean hasNext() {
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mPosition < mLength;
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String next() {
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = mString.indexOf(mDelimiter, mPosition);
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end == -1) {
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end = mLength;
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String nextString = mString.substring(mPosition, end);
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPosition = end + 1; // Skip the delimiter.
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return nextString;
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void remove() {
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new UnsupportedOperationException();
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence stringOrSpannedString(CharSequence source) {
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source == null)
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof SpannedString)
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return source;
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (source instanceof Spanned)
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new SpannedString(source);
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return source.toString();
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns true if the string is null or 0-length.
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param str the string to be examined
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if str is null or zero length
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isEmpty(CharSequence str) {
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (str == null || str.length() == 0)
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        else
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the length that the specified CharSequence would have if
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * spaces and control characters were trimmed from the start and end,
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * as by {@link String#trim}.
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int getTrimmedLength(CharSequence s) {
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = s.length();
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int start = 0;
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (start < len && s.charAt(start) <= ' ') {
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start++;
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end = len;
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (end > start && s.charAt(end - 1) <= ' ') {
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end--;
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return end - start;
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns true if a and b are equal, including if they are both null.
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p><i>Note: In platform versions 1.1 and earlier, this method only worked well if
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * both the arguments were instances of String.</i></p>
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param a first CharSequence to check
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param b second CharSequence to check
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if a and b are equal
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean equals(CharSequence a, CharSequence b) {
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (a == b) return true;
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int length;
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (a != null && b != null && (length = a.length()) == b.length()) {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (a instanceof String && b instanceof String) {
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return a.equals(b);
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < length; i++) {
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (a.charAt(i) != b.charAt(i)) return false;
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // XXX currently this only reverses chars, not spans
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence getReverse(CharSequence source,
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                          int start, int end) {
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new Reverser(source, start, end);
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Reverser
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    implements CharSequence, GetChars
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Reverser(CharSequence source, int start, int end) {
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSource = source;
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStart = start;
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mEnd = end;
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int length() {
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mEnd - mStart;
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence subSequence(int start, int end) {
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char[] buf = new char[end - start];
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(start, end, buf, 0);
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new String(buf);
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return subSequence(0, length()).toString();
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public char charAt(int off) {
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return AndroidCharacter.getMirror(mSource.charAt(mEnd - 1 - off));
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void getChars(int start, int end, char[] dest, int destoff) {
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextUtils.getChars(mSource, start + mStart, end + mStart,
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                               dest, destoff);
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            AndroidCharacter.mirror(dest, 0, end - start);
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int len = end - start;
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int n = (end - start) / 2;
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < n; i++) {
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                char tmp = dest[destoff + i];
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest[destoff + i] = dest[destoff + len - i - 1];
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dest[destoff + len - i - 1] = tmp;
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private CharSequence mSource;
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mStart;
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mEnd;
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int ALIGNMENT_SPAN = 1;
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int FOREGROUND_COLOR_SPAN = 2;
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int RELATIVE_SIZE_SPAN = 3;
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int SCALE_X_SPAN = 4;
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int STRIKETHROUGH_SPAN = 5;
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int UNDERLINE_SPAN = 6;
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int STYLE_SPAN = 7;
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int BULLET_SPAN = 8;
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int QUOTE_SPAN = 9;
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int LEADING_MARGIN_SPAN = 10;
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int URL_SPAN = 11;
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int BACKGROUND_COLOR_SPAN = 12;
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int TYPEFACE_SPAN = 13;
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int SUPERSCRIPT_SPAN = 14;
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int SUBSCRIPT_SPAN = 15;
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int ABSOLUTE_SIZE_SPAN = 16;
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int TEXT_APPEARANCE_SPAN = 17;
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int ANNOTATION = 18;
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Flatten a CharSequence and whatever styles can be copied across processes
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * into the parcel.
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void writeToParcel(CharSequence cs, Parcel p,
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int parcelableFlags) {
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cs instanceof Spanned) {
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.writeInt(0);
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.writeString(cs.toString());
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Spanned sp = (Spanned) cs;
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Object[] os = sp.getSpans(0, cs.length(), Object.class);
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // note to people adding to this: check more specific types
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // before more generic types.  also notice that it uses
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // "if" instead of "else if" where there are interfaces
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // so one object can be several.
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < os.length; i++) {
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object o = os[i];
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object prop = os[i];
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (prop instanceof CharacterStyle) {
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    prop = ((CharacterStyle) prop).getUnderlying();
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (prop instanceof ParcelableSpan) {
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ParcelableSpan ps = (ParcelableSpan)prop;
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    p.writeInt(ps.getSpanTypeId());
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ps.writeToParcel(p, parcelableFlags);
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    writeWhere(p, sp, o);
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.writeInt(0);
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.writeInt(1);
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cs != null) {
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                p.writeString(cs.toString());
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                p.writeString(null);
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void writeWhere(Parcel p, Spanned sp, Object o) {
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        p.writeInt(sp.getSpanStart(o));
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        p.writeInt(sp.getSpanEnd(o));
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        p.writeInt(sp.getSpanFlags(o));
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final Parcelable.Creator<CharSequence> CHAR_SEQUENCE_CREATOR
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = new Parcelable.Creator<CharSequence>() {
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Read and return a new CharSequence, possibly with styles,
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * from the parcel.
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public  CharSequence createFromParcel(Parcel p) {
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int kind = p.readInt();
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (kind == 1)
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return p.readString();
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SpannableString sp = new SpannableString(p.readString());
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (true) {
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                kind = p.readInt();
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (kind == 0)
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                switch (kind) {
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case ALIGNMENT_SPAN:
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new AlignmentSpan.Standard(p));
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case FOREGROUND_COLOR_SPAN:
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new ForegroundColorSpan(p));
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case RELATIVE_SIZE_SPAN:
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new RelativeSizeSpan(p));
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case SCALE_X_SPAN:
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new ScaleXSpan(p));
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case STRIKETHROUGH_SPAN:
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new StrikethroughSpan(p));
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case UNDERLINE_SPAN:
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new UnderlineSpan(p));
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case STYLE_SPAN:
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new StyleSpan(p));
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case BULLET_SPAN:
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new BulletSpan(p));
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case QUOTE_SPAN:
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new QuoteSpan(p));
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case LEADING_MARGIN_SPAN:
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new LeadingMarginSpan.Standard(p));
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case URL_SPAN:
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new URLSpan(p));
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case BACKGROUND_COLOR_SPAN:
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new BackgroundColorSpan(p));
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case TYPEFACE_SPAN:
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new TypefaceSpan(p));
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case SUPERSCRIPT_SPAN:
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new SuperscriptSpan(p));
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case SUBSCRIPT_SPAN:
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new SubscriptSpan(p));
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case ABSOLUTE_SIZE_SPAN:
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new AbsoluteSizeSpan(p));
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case TEXT_APPEARANCE_SPAN:
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new TextAppearanceSpan(p));
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case ANNOTATION:
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    readSpan(p, sp, new Annotation(p));
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                default:
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    throw new RuntimeException("bogus span encoding " + kind);
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sp;
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public CharSequence[] newArray(int size)
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        {
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return new CharSequence[size];
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Debugging tool to print the spans in a CharSequence.  The output will
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be printed one span per line.  If the CharSequence is not a Spanned,
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * then the entire string will be printed on a single line.
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void dumpSpans(CharSequence cs, Printer printer, String prefix) {
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cs instanceof Spanned) {
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Spanned sp = (Spanned) cs;
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Object[] os = sp.getSpans(0, cs.length(), Object.class);
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < os.length; i++) {
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object o = os[i];
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                printer.println(prefix + cs.subSequence(sp.getSpanStart(o),
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sp.getSpanEnd(o)) + ": "
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        + Integer.toHexString(System.identityHashCode(o))
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        + " " + o.getClass().getCanonicalName()
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         + " (" + sp.getSpanStart(o) + "-" + sp.getSpanEnd(o)
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         + ") fl=#" + sp.getSpanFlags(o));
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            printer.println(prefix + cs + ": (no spans)");
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Return a new CharSequence in which each of the source strings is
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * replaced by the corresponding element of the destinations.
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence replace(CharSequence template,
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       String[] sources,
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       CharSequence[] destinations) {
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpannableStringBuilder tb = new SpannableStringBuilder(template);
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < sources.length; i++) {
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int where = indexOf(tb, sources[i]);
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (where >= 0)
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                tb.setSpan(sources[i], where, where + sources[i].length(),
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                           Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < sources.length; i++) {
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start = tb.getSpanStart(sources[i]);
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = tb.getSpanEnd(sources[i]);
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start >= 0) {
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                tb.replace(start, end, destinations[i]);
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return tb;
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Replace instances of "^1", "^2", etc. in the
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>template</code> CharSequence with the corresponding
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>values</code>.  "^^" is used to produce a single caret in
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the output.  Only up to 9 replacement values are supported,
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * "^10" will be produce the first replacement value followed by a
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * '0'.
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param template the input text containing "^1"-style
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * placeholder values.  This object is not modified; a copy is
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * returned.
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param values CharSequences substituted into the template.  The
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * first is substituted for "^1", the second for "^2", and so on.
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the new CharSequence produced by doing the replacement
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws IllegalArgumentException if the template requests a
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * value that was not provided, or if more than 9 values are
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * provided.
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence expandTemplate(CharSequence template,
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              CharSequence... values) {
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (values.length > 9) {
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException("max of 9 values are supported");
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpannableStringBuilder ssb = new SpannableStringBuilder(template);
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i = 0;
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (i < ssb.length()) {
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (ssb.charAt(i) == '^') {
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char next = ssb.charAt(i+1);
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (next == '^') {
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ssb.delete(i+1, i+2);
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ++i;
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else if (Character.isDigit(next)) {
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int which = Character.getNumericValue(next) - 1;
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (which < 0) {
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            throw new IllegalArgumentException(
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "template requests value ^" + (which+1));
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (which >= values.length) {
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            throw new IllegalArgumentException(
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "template requests value ^" + (which+1) +
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                "; only " + values.length + " provided");
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ssb.replace(i, i+2, values[which]);
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        i += values[which].length();
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ++i;
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IndexOutOfBoundsException ignore) {
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // happens when ^ is the last character in the string.
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ssb;
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int getOffsetBefore(CharSequence text, int offset) {
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == 0)
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == 1)
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = text.charAt(offset - 1);
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c >= '\uDC00' && c <= '\uDFFF') {
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c1 = text.charAt(offset - 2);
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c1 >= '\uD800' && c1 <= '\uDBFF')
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset -= 2;
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset -= 1;
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            offset -= 1;
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text instanceof Spanned) {
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset,
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                       ReplacementSpan.class);
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < spans.length; i++) {
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = ((Spanned) text).getSpanStart(spans[i]);
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = ((Spanned) text).getSpanEnd(spans[i]);
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start < offset && end > offset)
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    offset = start;
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return offset;
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int getOffsetAfter(CharSequence text, int offset) {
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == len)
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return len;
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (offset == len - 1)
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return len;
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c = text.charAt(offset);
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (c >= '\uD800' && c <= '\uDBFF') {
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c1 = text.charAt(offset + 1);
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c1 >= '\uDC00' && c1 <= '\uDFFF')
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset += 2;
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                offset += 1;
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            offset += 1;
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text instanceof Spanned) {
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ReplacementSpan[] spans = ((Spanned) text).getSpans(offset, offset,
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                       ReplacementSpan.class);
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < spans.length; i++) {
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int start = ((Spanned) text).getSpanStart(spans[i]);
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int end = ((Spanned) text).getSpanEnd(spans[i]);
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (start < offset && end > offset)
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    offset = end;
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return offset;
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void readSpan(Parcel p, Spannable sp, Object o) {
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sp.setSpan(o, p.readInt(), p.readInt(), p.readInt());
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
919c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa    /**
920c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * Copies the spans from the region <code>start...end</code> in
921c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * <code>source</code> to the region
922c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * <code>destoff...destoff+end-start</code> in <code>dest</code>.
923c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * Spans in <code>source</code> that begin before <code>start</code>
924c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * or end after <code>end</code> but overlap this range are trimmed
925c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * as if they began at <code>start</code> or ended at <code>end</code>.
926c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     *
927c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * @throws IndexOutOfBoundsException if any of the copied spans
928c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     * are out of range in <code>dest</code>.
929c1d2748d442f06a7266be04b9e9c7d20609ad5ccDaisuke Miyakawa     */
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void copySpansFrom(Spanned source, int start, int end,
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                     Class kind,
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                     Spannable dest, int destoff) {
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (kind == null) {
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            kind = Object.class;
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] spans = source.getSpans(start, end, kind);
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < spans.length; i++) {
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int st = source.getSpanStart(spans[i]);
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int en = source.getSpanEnd(spans[i]);
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int fl = source.getSpanFlags(spans[i]);
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (st < start)
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                st = start;
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (en > end)
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                en = end;
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.setSpan(spans[i], st - start + destoff, en - start + destoff,
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         fl);
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum TruncateAt {
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        START,
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MIDDLE,
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        END,
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MARQUEE,
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public interface EllipsizeCallback {
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This method is called to report that the specified region of
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * text was ellipsized away by a call to {@link #ellipsize}.
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void ellipsized(int start, int end);
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sEllipsis = null;
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the original text if it fits in the specified width
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * given the properties of the specified Paint,
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * or, if it does not fit, a truncated
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * copy with ellipsis character added at the specified edge or center.
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence ellipsize(CharSequence text,
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         TextPaint p,
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         float avail, TruncateAt where) {
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ellipsize(text, p, avail, where, false, null);
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the original text if it fits in the specified width
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * given the properties of the specified Paint,
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * or, if it does not fit, a copy with ellipsis character added
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * at the specified edge or center.
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If <code>preserveLength</code> is specified, the returned copy
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * will be padded with zero-width spaces to preserve the original
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * length and offsets instead of truncating.
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If <code>callback</code> is non-null, it will be called to
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * report the start and end of the ellipsized range.
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence ellipsize(CharSequence text,
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         TextPaint p,
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         float avail, TruncateAt where,
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         boolean preserveLength,
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         EllipsizeCallback callback) {
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sEllipsis == null) {
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Resources r = Resources.getSystem();
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sEllipsis = r.getString(R.string.ellipsis);
10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Use Paint.breakText() for the non-Spanned case to avoid having
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // to allocate memory and accumulate the character widths ourselves.
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!(text instanceof Spanned)) {
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float wid = p.measureText(text, 0, len);
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (wid <= avail) {
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (callback != null) {
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    callback.ellipsized(0, 0);
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return text;
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float ellipsiswid = p.measureText(sEllipsis);
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ellipsiswid > avail) {
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (callback != null) {
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    callback.ellipsized(0, len);
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (preserveLength) {
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    char[] buf = obtain(len);
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (int i = 0; i < len; i++) {
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        buf[i] = '\uFEFF';
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String ret = new String(buf, 0, len);
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    recycle(buf);
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return ret;
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return "";
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (where == TruncateAt.START) {
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int fit = p.breakText(text, 0, len, false,
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                      avail - ellipsiswid, null);
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (callback != null) {
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    callback.ellipsized(0, len - fit);
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (preserveLength) {
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return blank(text, 0, len - fit);
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return sEllipsis + text.toString().substring(len - fit, len);
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (where == TruncateAt.END) {
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int fit = p.breakText(text, 0, len, true,
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                      avail - ellipsiswid, null);
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (callback != null) {
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    callback.ellipsized(fit, len);
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (preserveLength) {
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return blank(text, fit, len);
10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return text.toString().substring(0, fit) + sEllipsis;
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else /* where == TruncateAt.MIDDLE */ {
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int right = p.breakText(text, 0, len, false,
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        (avail - ellipsiswid) / 2, null);
10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float used = p.measureText(text, len - right, len);
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int left = p.breakText(text, 0, len - right, true,
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                       avail - ellipsiswid - used, null);
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (callback != null) {
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    callback.ellipsized(left, len - right);
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (preserveLength) {
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return blank(text, left, len - right);
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String s = text.toString();
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return s.substring(0, left) + sEllipsis +
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                           s.substring(len - right, len);
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // But do the Spanned cases by hand, because it's such a pain
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // to iterate the span transitions backwards and getTextWidths()
10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // will give us the information we need.
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // getTextWidths() always writes into the start of the array,
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // so measure each span into the first half and then copy the
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // results into the second half to use later.
10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float[] wid = new float[len * 2];
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TextPaint temppaint = new TextPaint();
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Spanned sp = (Spanned) text;
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int next;
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < len; i = next) {
11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            next = sp.nextSpanTransition(i, len, MetricAffectingSpan.class);
11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Styled.getTextWidths(p, temppaint, sp, i, next, wid, null);
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(wid, 0, wid, len + i, next - i);
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float sum = 0;
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < len; i++) {
11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sum += wid[len + i];
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sum <= avail) {
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (callback != null) {
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                callback.ellipsized(0, 0);
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return text;
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float ellipsiswid = p.measureText(sEllipsis);
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ellipsiswid > avail) {
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (callback != null) {
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                callback.ellipsized(0, len);
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (preserveLength) {
11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                char[] buf = obtain(len);
11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int i = 0; i < len; i++) {
11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    buf[i] = '\uFEFF';
11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                SpannableString ss = new SpannableString(new String(buf, 0, len));
11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                recycle(buf);
11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                copySpansFrom(sp, 0, len, Object.class, ss, 0);
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return ss;
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return "";
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where == TruncateAt.START) {
11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sum = 0;
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i;
11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (i = len; i >= 0; i--) {
11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float w = wid[len + i - 1];
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (w + sum + ellipsiswid > avail) {
11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sum += w;
11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (callback != null) {
11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                callback.ellipsized(0, i);
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (preserveLength) {
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                SpannableString ss = new SpannableString(blank(text, 0, i));
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                copySpansFrom(sp, 0, len, Object.class, ss, 0);
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return ss;
11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                SpannableStringBuilder out = new SpannableStringBuilder(sEllipsis);
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.insert(1, text, i, len);
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return out;
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (where == TruncateAt.END) {
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sum = 0;
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int i;
11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (i = 0; i < len; i++) {
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float w = wid[len + i];
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (w + sum + ellipsiswid > avail) {
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sum += w;
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (callback != null) {
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                callback.ellipsized(i, len);
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (preserveLength) {
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                SpannableString ss = new SpannableString(blank(text, i, len));
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                copySpansFrom(sp, 0, len, Object.class, ss, 0);
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return ss;
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                SpannableStringBuilder out = new SpannableStringBuilder(sEllipsis);
11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.insert(0, text, 0, i);
11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return out;
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else /* where = TruncateAt.MIDDLE */ {
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float lsum = 0, rsum = 0;
11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int left = 0, right = len;
12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float ravail = (avail - ellipsiswid) / 2;
12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (right = len; right >= 0; right--) {
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float w = wid[len + right - 1];
12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (w + rsum > ravail) {
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                rsum += w;
12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float lavail = avail - ellipsiswid - rsum;
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (left = 0; left < right; left++) {
12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float w = wid[len + left];
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (w + lsum > lavail) {
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                lsum += w;
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (callback != null) {
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                callback.ellipsized(left, right);
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (preserveLength) {
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                SpannableString ss = new SpannableString(blank(text, left, right));
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                copySpansFrom(sp, 0, len, Object.class, ss, 0);
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return ss;
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                SpannableStringBuilder out = new SpannableStringBuilder(sEllipsis);
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.insert(0, text, 0, left);
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.insert(out.length(), text, right, len);
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return out;
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String blank(CharSequence source, int start, int end) {
12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = source.length();
12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] buf = obtain(len);
12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start != 0) {
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(source, 0, start, buf, 0);
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (end != len) {
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            getChars(source, end, len, buf, end);
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (start != end) {
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            buf[start] = '\u2026';
12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = start + 1; i < end; i++) {
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                buf[i] = '\uFEFF';
12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String ret = new String(buf, 0, len);
12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recycle(buf);
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Converts a CharSequence of the comma-separated form "Andy, Bob,
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Charles, David" that is too wide to fit into the specified width
12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * into one like "Andy, Bob, 2 more".
12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param text the text to truncate
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param p the Paint with which to measure the text
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param avail the horizontal width available for the text
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param oneMore the string for "1 more" in the current locale
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param more the string for "%d more" in the current locale
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence commaEllipsize(CharSequence text,
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              TextPaint p, float avail,
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              String oneMore,
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                              String more) {
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] buf = new char[len];
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TextUtils.getChars(text, 0, len, buf, 0);
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int commaCount = 0;
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < len; i++) {
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (buf[i] == ',') {
12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                commaCount++;
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float[] wid;
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text instanceof Spanned) {
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Spanned sp = (Spanned) text;
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            TextPaint temppaint = new TextPaint();
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            wid = new float[len * 2];
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int next;
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < len; i = next) {
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                next = sp.nextSpanTransition(i, len, MetricAffectingSpan.class);
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Styled.getTextWidths(p, temppaint, sp, i, next, wid, null);
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                System.arraycopy(wid, 0, wid, len + i, next - i);
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(wid, len, wid, 0, len);
13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            wid = new float[len];
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            p.getTextWidths(text, 0, len, wid);
13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int ok = 0;
13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int okRemaining = commaCount + 1;
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String okFormat = "";
13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int w = 0;
13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = 0;
13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < len; i++) {
13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            w += wid[i];
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (buf[i] == ',') {
13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                count++;
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int remaining = commaCount - count + 1;
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float moreWid;
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String format;
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (remaining == 1) {
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    format = " " + oneMore;
13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    format = " " + String.format(more, remaining);
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                moreWid = p.measureText(format);
13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (w + moreWid <= avail) {
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ok = i + 1;
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    okRemaining = remaining;
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    okFormat = format;
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (w <= avail) {
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return text;
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            SpannableStringBuilder out = new SpannableStringBuilder(okFormat);
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.insert(0, text, 0, ok);
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return out;
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static char[] obtain(int len) {
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char[] buf;
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (sLock) {
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            buf = sTemp;
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sTemp = null;
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (buf == null || buf.length < len)
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            buf = new char[ArrayUtils.idealCharArraySize(len)];
13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return buf;
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* package */ static void recycle(char[] temp) {
13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (temp.length > 1000)
13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (sLock) {
13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sTemp = temp;
13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Html-encode the string.
13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param s the string to be encoded
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the encoded string
13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String htmlEncode(String s) {
13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c;
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < s.length(); i++) {
13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = s.charAt(i);
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (c) {
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '<':
13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&lt;"); //$NON-NLS-1$
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '>':
13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&gt;"); //$NON-NLS-1$
13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '&':
13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&amp;"); //$NON-NLS-1$
13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '\'':
13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&apos;"); //$NON-NLS-1$
14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case '"':
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("&quot;"); //$NON-NLS-1$
14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            default:
14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(c);
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a CharSequence concatenating the specified CharSequences,
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * retaining their spans if any.
14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CharSequence concat(CharSequence... text) {
14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text.length == 0) {
14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "";
14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (text.length == 1) {
14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return text[0];
14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean spanned = false;
14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < text.length; i++) {
14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (text[i] instanceof Spanned) {
14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                spanned = true;
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
14339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < text.length; i++) {
14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sb.append(text[i]);
14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!spanned) {
14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sb.toString();
14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SpannableString ss = new SpannableString(sb);
14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int off = 0;
14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < text.length; i++) {
14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int len = text[i].length();
14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (text[i] instanceof Spanned) {
14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                copySpansFrom((Spanned) text[i], 0, len, Object.class, ss, off);
14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            off += len;
14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new SpannedString(ss);
14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns whether the given CharSequence contains any printable characters.
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isGraphic(CharSequence str) {
14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int len = str.length();
14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i=0; i<len; i++) {
14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int gc = Character.getType(str.charAt(i));
14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (gc != Character.CONTROL
14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.FORMAT
14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.SURROGATE
14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.UNASSIGNED
14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.LINE_SEPARATOR
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.PARAGRAPH_SEPARATOR
14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && gc != Character.SPACE_SEPARATOR) {
14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns whether this character is a printable character.
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isGraphic(char c) {
14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int gc = Character.getType(c);
14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return     gc != Character.CONTROL
14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.FORMAT
14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.SURROGATE
14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.UNASSIGNED
14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.LINE_SEPARATOR
14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.PARAGRAPH_SEPARATOR
14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && gc != Character.SPACE_SEPARATOR;
14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
14919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns whether the given CharSequence contains only digits.
14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean isDigitsOnly(CharSequence str) {
14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int len = str.length();
14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < len; i++) {
14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!Character.isDigit(str.charAt(i))) {
14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
14989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1504973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa     * @hide
1505973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa     */
1506973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    public static boolean isPrintableAscii(final char c) {
1507973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        final int asciiFirst = 0x20;
1508973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        final int asciiLast = 0x7E;  // included
1509973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        return (asciiFirst <= c && c <= asciiLast) || c == '\r' || c == '\n';
1510973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    }
1511973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa
1512973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    /**
1513973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa     * @hide
1514973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa     */
1515973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    public static boolean isPrintableAsciiOnly(final CharSequence str) {
1516973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        final int len = str.length();
1517973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        for (int i = 0; i < len; i++) {
1518973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa            if (!isPrintableAscii(str.charAt(i))) {
1519973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa                return false;
1520973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa            }
1521973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        }
1522973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa        return true;
1523973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    }
1524973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa
1525973afa96bf184be6b8b5a25963568650e70750f7Daisuke Miyakawa    /**
15269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Capitalization mode for {@link #getCapsMode}: capitalize all
15279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * characters.  This value is explicitly defined to be the same as
15289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link InputType#TYPE_TEXT_FLAG_CAP_CHARACTERS}.
15299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int CAP_MODE_CHARACTERS
15319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
15329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
15349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Capitalization mode for {@link #getCapsMode}: capitalize the first
15359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * character of all words.  This value is explicitly defined to be the same as
15369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link InputType#TYPE_TEXT_FLAG_CAP_WORDS}.
15379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int CAP_MODE_WORDS
15399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = InputType.TYPE_TEXT_FLAG_CAP_WORDS;
15409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
15429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Capitalization mode for {@link #getCapsMode}: capitalize the first
15439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * character of each sentence.  This value is explicitly defined to be the same as
15449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link InputType#TYPE_TEXT_FLAG_CAP_SENTENCES}.
15459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int CAP_MODE_SENTENCES
15479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
15489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
15509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Determine what caps mode should be in effect at the current offset in
15519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the text.  Only the mode bits set in <var>reqModes</var> will be
15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * checked.  Note that the caps mode flags here are explicitly defined
15539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to match those in {@link InputType}.
15549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param cs The text that should be checked for caps modes.
15569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param off Location in the text at which to check.
15579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param reqModes The modes to be checked: may be any combination of
15589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
15599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #CAP_MODE_SENTENCES}.
156060919953ce80dbf75673837ea51497c84da7ac78Mark Wagner     *
15619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Returns the actual capitalization modes that can be in effect
15629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * at the current position, which is any combination of
15639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
15649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #CAP_MODE_SENTENCES}.
15659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static int getCapsMode(CharSequence cs, int off, int reqModes) {
156760919953ce80dbf75673837ea51497c84da7ac78Mark Wagner        if (off < 0) {
156860919953ce80dbf75673837ea51497c84da7ac78Mark Wagner            return 0;
156960919953ce80dbf75673837ea51497c84da7ac78Mark Wagner        }
157060919953ce80dbf75673837ea51497c84da7ac78Mark Wagner
15719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int i;
15729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char c;
15739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int mode = 0;
15749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((reqModes&CAP_MODE_CHARACTERS) != 0) {
15769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mode |= CAP_MODE_CHARACTERS;
15779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((reqModes&(CAP_MODE_WORDS|CAP_MODE_SENTENCES)) == 0) {
15799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mode;
15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Back over allowed opening punctuation.
15839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (i = off; i > 0; i--) {
15859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = cs.charAt(i - 1);
15869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != '"' && c != '\'' &&
15889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Character.getType(c) != Character.START_PUNCTUATION) {
15899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
15909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Start of paragraph, with optional whitespace.
15949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int j = i;
15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (j > 0 && ((c = cs.charAt(j - 1)) == ' ' || c == '\t')) {
15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            j--;
15989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (j == 0 || cs.charAt(j - 1) == '\n') {
16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mode | CAP_MODE_WORDS;
16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Or start of word if we are that style.
16049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((reqModes&CAP_MODE_SENTENCES) == 0) {
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (i != j) mode |= CAP_MODE_WORDS;
16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mode;
16089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // There must be a space if not the start of paragraph.
16119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (i == j) {
16139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mode;
16149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Back over allowed closing punctuation.
16179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (; j > 0; j--) {
16199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = cs.charAt(j - 1);
16209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != '"' && c != '\'' &&
16229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Character.getType(c) != Character.END_PUNCTUATION) {
16239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
16249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (j > 0) {
16289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            c = cs.charAt(j - 1);
16299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == '.' || c == '?' || c == '!') {
16319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Do not capitalize if the word ends with a period but
16329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // also contains a period, in which case it is an abbreviation.
16339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (c == '.') {
16359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    for (int k = j - 2; k >= 0; k--) {
16369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        c = cs.charAt(k);
16379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (c == '.') {
16399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            return mode;
16409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
16419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (!Character.isLetter(c)) {
16439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
16449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
16459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
16469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
16479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mode | CAP_MODE_SENTENCES;
16499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mode;
16539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Object sLock = new Object();
16569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static char[] sTemp = null;
16579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1658