/* * 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 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)) { removeFromSuggestions(suggestions, i); i--; break; } } i++; } } private static void removeFromSuggestions(final ArrayList suggestions, final int index) { final CharSequence garbage = suggestions.remove(index); if (garbage instanceof StringBuilder) { StringBuilderPool.recycle((StringBuilder)garbage); } } 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); } }