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