blob: c82e0d7b7c97183cda7507d60a5d57821d39c74b [file] [log] [blame]
Chiao Chenga6b4c792012-10-31 15:18:29 -07001/*
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 */
Gary Mai69c182a2016-12-05 13:07:03 -080016package com.android.contacts.format;
Chiao Chenga6b4c792012-10-31 15:18:29 -070017
18import android.database.CharArrayBuffer;
19import android.graphics.Typeface;
20import android.text.SpannableString;
21import android.text.style.StyleSpan;
22
23import com.google.common.annotations.VisibleForTesting;
24
25import java.util.Arrays;
26
27/**
28 * Assorted utility methods related to text formatting in Contacts.
29 */
30public 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 */
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
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 */
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 */
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
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
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 */
Chiao Chenga1554ef2012-12-21 15:45:54 -0800141 public static int indexOfWordPrefix(CharSequence text, String prefix) {
Chiao Chenga6b4c792012-10-31 15:18:29 -0700142 if (prefix == null || text == null) {
143 return -1;
144 }
145
146 int textLength = text.length();
Chiao Chenga1554ef2012-12-21 15:45:54 -0800147 int prefixLength = prefix.length();
Chiao Chenga6b4c792012-10-31 15:18:29 -0700148
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++) {
Chiao Chenga1554ef2012-12-21 15:45:54 -0800167 if (Character.toUpperCase(text.charAt(i + j)) != prefix.charAt(j)) {
Chiao Chenga6b4c792012-10-31 15:18:29 -0700168 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}