1a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng/*
2a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * Copyright (C) 2011 The Android Open Source Project
3a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng *
4a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * Licensed under the Apache License, Version 2.0 (the "License");
5a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * you may not use this file except in compliance with the License.
6a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * You may obtain a copy of the License at
7a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng *
8a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng *      http://www.apache.org/licenses/LICENSE-2.0
9a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng *
10a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * Unless required by applicable law or agreed to in writing, software
11a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * distributed under the License is distributed on an "AS IS" BASIS,
12a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * See the License for the specific language governing permissions and
14a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * limitations under the License.
15a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng */
16a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Chengpackage com.android.contacts.common.format;
17a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
18a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Chengimport android.database.CharArrayBuffer;
19a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Chengimport android.graphics.Typeface;
20a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Chengimport android.text.SpannableString;
21a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Chengimport android.text.style.StyleSpan;
22a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
23a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Chengimport com.google.common.annotations.VisibleForTesting;
24a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
25a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Chengimport java.util.Arrays;
26a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
27a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng/**
28a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng * Assorted utility methods related to text formatting in Contacts.
29a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng */
30a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Chengpublic class FormatUtils {
31a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
32a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    /**
33a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * Finds the earliest point in buffer1 at which the first part of buffer2 matches.  For example,
34a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * overlapPoint("abcd", "cdef") == 2.
35a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     */
36a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    public static int overlapPoint(CharArrayBuffer buffer1, CharArrayBuffer buffer2) {
37a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        if (buffer1 == null || buffer2 == null) {
38a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            return -1;
39a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
40a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        return overlapPoint(Arrays.copyOfRange(buffer1.data, 0, buffer1.sizeCopied),
41a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                Arrays.copyOfRange(buffer2.data, 0, buffer2.sizeCopied));
42a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    }
43a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
44a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    /**
45a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * Finds the earliest point in string1 at which the first part of string2 matches.  For example,
46a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * overlapPoint("abcd", "cdef") == 2.
47a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     */
48a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    @VisibleForTesting
49a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    public static int overlapPoint(String string1, String string2) {
50a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        if (string1 == null || string2 == null) {
51a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            return -1;
52a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
53a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        return overlapPoint(string1.toCharArray(), string2.toCharArray());
54a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    }
55a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
56a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    /**
57a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * Finds the earliest point in array1 at which the first part of array2 matches.  For example,
58a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * overlapPoint("abcd", "cdef") == 2.
59a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     */
60a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    public static int overlapPoint(char[] array1, char[] array2) {
61a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        if (array1 == null || array2 == null) {
62a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            return -1;
63a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
64a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        int count1 = array1.length;
65a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        int count2 = array2.length;
66a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
67a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        // Ignore matching tails of the two arrays.
68a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        while (count1 > 0 && count2 > 0 && array1[count1 - 1] == array2[count2 - 1]) {
69a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            count1--;
70a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            count2--;
71a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
72a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
73a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        int size = count2;
74a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        for (int i = 0; i < count1; i++) {
75a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            if (i + size > count1) {
76a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                size = count1 - i;
77a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
78a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            int j;
79a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            for (j = 0; j < size; j++) {
80a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                if (array1[i+j] != array2[j]) {
81a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                    break;
82a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                }
83a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
84a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            if (j == size) {
85a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                return i;
86a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
87a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
88a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
89a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        return -1;
90a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    }
91a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
92a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    /**
93a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * Applies the given style to a range of the input CharSequence.
94a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * @param style The style to apply (see the style constants in {@link Typeface}).
95a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * @param input The CharSequence to style.
96a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * @param start Starting index of the range to style (will be clamped to be a minimum of 0).
97a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * @param end Ending index of the range to style (will be clamped to a maximum of the input
98a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     *     length).
99a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * @param flags Bitmask for configuring behavior of the span.  See {@link android.text.Spanned}.
100a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * @return The styled CharSequence.
101a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     */
102a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    public static CharSequence applyStyleToSpan(int style, CharSequence input, int start, int end,
103a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            int flags) {
104a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        // Enforce bounds of the char sequence.
105a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        start = Math.max(0, start);
106a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        end = Math.min(input.length(), end);
107a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        SpannableString text = new SpannableString(input);
108a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        text.setSpan(new StyleSpan(style), start, end, flags);
109a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        return text;
110a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    }
111a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
112a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    @VisibleForTesting
113a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    public static void copyToCharArrayBuffer(String text, CharArrayBuffer buffer) {
114a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        if (text != null) {
115a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            char[] data = buffer.data;
116a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            if (data == null || data.length < text.length()) {
117a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                buffer.data = text.toCharArray();
118a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            } else {
119a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                text.getChars(0, text.length(), data, 0);
120a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
121a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            buffer.sizeCopied = text.length();
122a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        } else {
123a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            buffer.sizeCopied = 0;
124a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
125a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    }
126a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
127a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    /** Returns a String that represents the content of the given {@link CharArrayBuffer}. */
128a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    @VisibleForTesting
129a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    public static String charArrayBufferToString(CharArrayBuffer buffer) {
130a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        return new String(buffer.data, 0, buffer.sizeCopied);
131a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    }
132a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
133a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    /**
134a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * Finds the index of the first word that starts with the given prefix.
135a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * <p>
136a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * If not found, returns -1.
137a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     *
138a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * @param text the text in which to search for the prefix
139a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     * @param prefix the text to find, in upper case letters
140a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng     */
141bd80fd64b9ff94c9ffbdb843beb4b363bb209463Chiao Cheng    public static int indexOfWordPrefix(CharSequence text, String prefix) {
142a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        if (prefix == null || text == null) {
143a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            return -1;
144a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
145a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
146a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        int textLength = text.length();
147bd80fd64b9ff94c9ffbdb843beb4b363bb209463Chiao Cheng        int prefixLength = prefix.length();
148a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
149a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        if (prefixLength == 0 || textLength < prefixLength) {
150a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            return -1;
151a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
152a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
153a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        int i = 0;
154a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        while (i < textLength) {
155a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            // Skip non-word characters
156a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            while (i < textLength && !Character.isLetterOrDigit(text.charAt(i))) {
157a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                i++;
158a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
159a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
160a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            if (i + prefixLength > textLength) {
161a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                return -1;
162a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
163a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
164a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            // Compare the prefixes
165a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            int j;
166a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            for (j = 0; j < prefixLength; j++) {
167bd80fd64b9ff94c9ffbdb843beb4b363bb209463Chiao Cheng                if (Character.toUpperCase(text.charAt(i + j)) != prefix.charAt(j)) {
168a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                    break;
169a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                }
170a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
171a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            if (j == prefixLength) {
172a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                return i;
173a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
174a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
175a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            // Skip this word
176a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            while (i < textLength && Character.isLetterOrDigit(text.charAt(i))) {
177a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng                i++;
178a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng            }
179a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        }
180a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
181a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng        return -1;
182a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng    }
183a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng
184a8feb7b88f2f67d8a80762dc54d336f1ea3a22d3Chiao Cheng}
185