2012-03-08 08:07:02 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.inputmethod.latin;
|
|
|
|
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.view.inputmethod.EditorInfo;
|
|
|
|
|
|
|
|
import com.android.inputmethod.keyboard.Keyboard;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Locale;
|
|
|
|
|
|
|
|
public class StringUtils {
|
|
|
|
private StringUtils() {
|
|
|
|
// This utility class is not publicly instantiable.
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean canBeFollowedByPeriod(final int codePoint) {
|
|
|
|
// TODO: Check again whether there really ain't a better way to check this.
|
|
|
|
// TODO: This should probably be language-dependant...
|
|
|
|
return Character.isLetterOrDigit(codePoint)
|
|
|
|
|| codePoint == Keyboard.CODE_SINGLE_QUOTE
|
|
|
|
|| codePoint == Keyboard.CODE_DOUBLE_QUOTE
|
|
|
|
|| codePoint == Keyboard.CODE_CLOSING_PARENTHESIS
|
|
|
|
|| codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET
|
|
|
|
|| codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET
|
|
|
|
|| codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static int codePointCount(String text) {
|
|
|
|
if (TextUtils.isEmpty(text)) return 0;
|
|
|
|
return text.codePointCount(0, text.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean containsInCsv(String key, String csv) {
|
|
|
|
if (csv == null)
|
|
|
|
return false;
|
|
|
|
for (String option : csv.split(",")) {
|
|
|
|
if (option.equals(key))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean inPrivateImeOptions(String packageName, String key,
|
|
|
|
EditorInfo editorInfo) {
|
|
|
|
if (editorInfo == null)
|
|
|
|
return false;
|
|
|
|
return containsInCsv(packageName != null ? packageName + "." + key : key,
|
|
|
|
editorInfo.privateImeOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if a and b are equal ignoring the case of the character.
|
|
|
|
* @param a first character to check
|
|
|
|
* @param b second character to check
|
|
|
|
* @return {@code true} if a and b are equal, {@code false} otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean equalsIgnoreCase(char a, char b) {
|
|
|
|
// Some language, such as Turkish, need testing both cases.
|
|
|
|
return a == b
|
|
|
|
|| Character.toLowerCase(a) == Character.toLowerCase(b)
|
|
|
|
|| Character.toUpperCase(a) == Character.toUpperCase(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if a and b are equal ignoring the case of the characters, including if they are
|
|
|
|
* both null.
|
|
|
|
* @param a first CharSequence to check
|
|
|
|
* @param b second CharSequence to check
|
|
|
|
* @return {@code true} if a and b are equal, {@code false} otherwise.
|
|
|
|
*/
|
|
|
|
public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) {
|
|
|
|
if (a == b)
|
|
|
|
return true; // including both a and b are null.
|
|
|
|
if (a == null || b == null)
|
|
|
|
return false;
|
|
|
|
final int length = a.length();
|
|
|
|
if (length != b.length())
|
|
|
|
return false;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
if (!equalsIgnoreCase(a.charAt(i), b.charAt(i)))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if a and b are equal ignoring the case of the characters, including if a is null
|
|
|
|
* and b is zero length.
|
|
|
|
* @param a CharSequence to check
|
|
|
|
* @param b character array to check
|
|
|
|
* @param offset start offset of array b
|
|
|
|
* @param length length of characters in array b
|
|
|
|
* @return {@code true} if a and b are equal, {@code false} otherwise.
|
|
|
|
* @throws IndexOutOfBoundsException
|
|
|
|
* if {@code offset < 0 || length < 0 || offset + length > data.length}.
|
|
|
|
* @throws NullPointerException if {@code b == null}.
|
|
|
|
*/
|
|
|
|
public static boolean equalsIgnoreCase(CharSequence a, char[] b, int offset, int length) {
|
|
|
|
if (offset < 0 || length < 0 || length > b.length - offset)
|
|
|
|
throw new IndexOutOfBoundsException("array.length=" + b.length + " offset=" + offset
|
|
|
|
+ " length=" + length);
|
|
|
|
if (a == null)
|
|
|
|
return length == 0; // including a is null and b is zero length.
|
|
|
|
if (a.length() != length)
|
|
|
|
return false;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
if (!equalsIgnoreCase(a.charAt(i), b[offset + i]))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove duplicates from an array of strings.
|
|
|
|
*
|
|
|
|
* This method will always keep the first occurence of all strings at their position
|
|
|
|
* in the array, removing the subsequent ones.
|
|
|
|
*/
|
|
|
|
public static void removeDupes(final ArrayList<CharSequence> suggestions) {
|
|
|
|
if (suggestions.size() < 2) return;
|
|
|
|
int i = 1;
|
|
|
|
// Don't cache suggestions.size(), since we may be removing items
|
|
|
|
while (i < suggestions.size()) {
|
|
|
|
final CharSequence cur = suggestions.get(i);
|
|
|
|
// Compare each suggestion with each previous suggestion
|
|
|
|
for (int j = 0; j < i; j++) {
|
|
|
|
CharSequence previous = suggestions.get(j);
|
|
|
|
if (TextUtils.equals(cur, previous)) {
|
2012-03-13 07:52:42 +00:00
|
|
|
suggestions.remove(i);
|
2012-03-08 08:07:02 +00:00
|
|
|
i--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
|
|
|
|
if (returnsNameInThisLocale) {
|
|
|
|
return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
|
|
|
|
} else {
|
|
|
|
return toTitleCase(locale.getDisplayName(), locale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String getDisplayLanguage(Locale locale) {
|
|
|
|
return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String getMiddleDisplayLanguage(Locale locale) {
|
|
|
|
return toTitleCase((LocaleUtils.constructLocaleFromString(
|
|
|
|
locale.getLanguage()).getDisplayLanguage(locale)), locale);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String getShortDisplayLanguage(Locale locale) {
|
|
|
|
return toTitleCase(locale.getLanguage(), locale);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String toTitleCase(String s, Locale locale) {
|
|
|
|
if (s.length() <= 1) {
|
|
|
|
// TODO: is this really correct? Shouldn't this be s.toUpperCase()?
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
// TODO: fix the bugs below
|
|
|
|
// - This does not work for Greek, because it returns upper case instead of title case.
|
|
|
|
// - It does not work for Serbian, because it fails to account for the "lj" character,
|
|
|
|
// which should be "Lj" in title case and "LJ" in upper case.
|
|
|
|
// - It does not work for Dutch, because it fails to account for the "ij" digraph, which
|
|
|
|
// are two different characters but both should be capitalized as "IJ" as if they were
|
|
|
|
// a single letter.
|
|
|
|
// - It also does not work with unicode surrogate code points.
|
|
|
|
return s.toUpperCase(locale).charAt(0) + s.substring(1);
|
|
|
|
}
|
|
|
|
}
|