Fix test breakage
This CL also adds null analysis annotations to StringUtils. Change-Id: I751932c1ed2579bc10f4584651b997356f180899main
parent
48ee473c1e
commit
80980574ac
|
@ -14,8 +14,9 @@
|
||||||
|
|
||||||
LOCAL_PATH:= $(call my-dir)
|
LOCAL_PATH:= $(call my-dir)
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
|
||||||
LOCAL_MODULE := latinime-common
|
LOCAL_MODULE := latinime-common
|
||||||
|
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||||
|
LOCAL_STATIC_JAVA_LIBRARIES := jsr305
|
||||||
LOCAL_SDK_VERSION := 21
|
LOCAL_SDK_VERSION := 21
|
||||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||||
|
|
||||||
|
@ -23,4 +24,5 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := latinime-common-host
|
LOCAL_MODULE := latinime-common-host
|
||||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||||
|
LOCAL_STATIC_JAVA_LIBRARIES := jsr305lib
|
||||||
include $(BUILD_HOST_JAVA_LIBRARY)
|
include $(BUILD_HOST_JAVA_LIBRARY)
|
||||||
|
|
|
@ -22,11 +22,15 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public final class StringUtils {
|
public final class StringUtils {
|
||||||
public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
|
public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
|
||||||
public static final int CAPITALIZE_FIRST = 1; // First only
|
public static final int CAPITALIZE_FIRST = 1; // First only
|
||||||
public static final int CAPITALIZE_ALL = 2; // All caps
|
public static final int CAPITALIZE_ALL = 2; // All caps
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
private static final String EMPTY_STRING = "";
|
private static final String EMPTY_STRING = "";
|
||||||
|
|
||||||
private static final char CHAR_LINE_FEED = 0X000A;
|
private static final char CHAR_LINE_FEED = 0X000A;
|
||||||
|
@ -48,23 +52,23 @@ public final class StringUtils {
|
||||||
* @param str the string to be examined
|
* @param str the string to be examined
|
||||||
* @return true if str is null or zero length
|
* @return true if str is null or zero length
|
||||||
*/
|
*/
|
||||||
public static boolean isEmpty(CharSequence str) {
|
public static boolean isEmpty(@Nullable final CharSequence str) {
|
||||||
if (str == null || str.length() == 0)
|
return (str == null || str.length() == 0);
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taken from android.text.TextUtils to cut the dependency to the Android framework.
|
// Taken from android.text.TextUtils to cut the dependency to the Android framework.
|
||||||
/**
|
/**
|
||||||
* Returns a string containing the tokens joined by delimiters.
|
* Returns a string containing the tokens joined by delimiters.
|
||||||
|
* @param delimiter the delimiter
|
||||||
* @param tokens an array objects to be joined. Strings will be formed from
|
* @param tokens an array objects to be joined. Strings will be formed from
|
||||||
* the objects by calling object.toString().
|
* the objects by calling object.toString().
|
||||||
*/
|
*/
|
||||||
public static String join(CharSequence delimiter, Iterable tokens) {
|
@Nonnull
|
||||||
StringBuilder sb = new StringBuilder();
|
public static String join(@Nonnull final CharSequence delimiter,
|
||||||
|
@Nonnull final Iterable<?> tokens) {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
boolean firstTime = true;
|
boolean firstTime = true;
|
||||||
for (Object token: tokens) {
|
for (final Object token: tokens) {
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
firstTime = false;
|
firstTime = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -84,28 +88,34 @@ public final class StringUtils {
|
||||||
* @param b second CharSequence to check
|
* @param b second CharSequence to check
|
||||||
* @return true if a and b are equal
|
* @return true if a and b are equal
|
||||||
*/
|
*/
|
||||||
public static boolean equals(CharSequence a, CharSequence b) {
|
public static boolean equals(@Nullable final CharSequence a, @Nullable final CharSequence b) {
|
||||||
if (a == b) return true;
|
if (a == b) {
|
||||||
int length;
|
return true;
|
||||||
|
}
|
||||||
|
final int length;
|
||||||
if (a != null && b != null && (length = a.length()) == b.length()) {
|
if (a != null && b != null && (length = a.length()) == b.length()) {
|
||||||
if (a instanceof String && b instanceof String) {
|
if (a instanceof String && b instanceof String) {
|
||||||
return a.equals(b);
|
return a.equals(b);
|
||||||
} else {
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
if (a.charAt(i) != b.charAt(i)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (a.charAt(i) != b.charAt(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int codePointCount(final CharSequence text) {
|
public static int codePointCount(@Nullable final CharSequence text) {
|
||||||
if (isEmpty(text)) return 0;
|
if (isEmpty(text)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return Character.codePointCount(text, 0, text.length());
|
return Character.codePointCount(text, 0, text.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String newSingleCodePointString(int codePoint) {
|
@Nonnull
|
||||||
|
public static String newSingleCodePointString(final int codePoint) {
|
||||||
if (Character.charCount(codePoint) == 1) {
|
if (Character.charCount(codePoint) == 1) {
|
||||||
// Optimization: avoid creating a temporary array for characters that are
|
// Optimization: avoid creating a temporary array for characters that are
|
||||||
// represented by a single char value
|
// represented by a single char value
|
||||||
|
@ -115,9 +125,12 @@ public final class StringUtils {
|
||||||
return new String(Character.toChars(codePoint));
|
return new String(Character.toChars(codePoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean containsInArray(final String text, final String[] array) {
|
public static boolean containsInArray(@Nonnull final String text,
|
||||||
|
@Nonnull final String[] array) {
|
||||||
for (final String element : array) {
|
for (final String element : array) {
|
||||||
if (text.equals(element)) return true;
|
if (text.equals(element)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -127,18 +140,20 @@ public final class StringUtils {
|
||||||
* Unlike CSV, Comma-Splittable Text has no escaping mechanism, so that the text can't contain
|
* Unlike CSV, Comma-Splittable Text has no escaping mechanism, so that the text can't contain
|
||||||
* a comma character in it.
|
* a comma character in it.
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ",";
|
private static final String SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT = ",";
|
||||||
|
|
||||||
public static boolean containsInCommaSplittableText(final String text,
|
public static boolean containsInCommaSplittableText(@Nonnull final String text,
|
||||||
final String extraValues) {
|
@Nullable final String extraValues) {
|
||||||
if (isEmpty(extraValues)) {
|
if (isEmpty(extraValues)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT));
|
return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String removeFromCommaSplittableTextIfExists(final String text,
|
@Nonnull
|
||||||
final String extraValues) {
|
public static String removeFromCommaSplittableTextIfExists(@Nonnull final String text,
|
||||||
|
@Nullable final String extraValues) {
|
||||||
if (isEmpty(extraValues)) {
|
if (isEmpty(extraValues)) {
|
||||||
return EMPTY_STRING;
|
return EMPTY_STRING;
|
||||||
}
|
}
|
||||||
|
@ -161,8 +176,10 @@ public final class StringUtils {
|
||||||
* This method will always keep the first occurrence of all strings at their position
|
* This method will always keep the first occurrence of all strings at their position
|
||||||
* in the array, removing the subsequent ones.
|
* in the array, removing the subsequent ones.
|
||||||
*/
|
*/
|
||||||
public static void removeDupes(final ArrayList<String> suggestions) {
|
public static void removeDupes(@Nonnull final ArrayList<String> suggestions) {
|
||||||
if (suggestions.size() < 2) return;
|
if (suggestions.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int i = 1;
|
int i = 1;
|
||||||
// Don't cache suggestions.size(), since we may be removing items
|
// Don't cache suggestions.size(), since we may be removing items
|
||||||
while (i < suggestions.size()) {
|
while (i < suggestions.size()) {
|
||||||
|
@ -180,7 +197,9 @@ public final class StringUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String capitalizeFirstCodePoint(final String s, final Locale locale) {
|
@Nonnull
|
||||||
|
public static String capitalizeFirstCodePoint(@Nonnull final String s,
|
||||||
|
@Nonnull final Locale locale) {
|
||||||
if (s.length() <= 1) {
|
if (s.length() <= 1) {
|
||||||
return s.toUpperCase(locale);
|
return s.toUpperCase(locale);
|
||||||
}
|
}
|
||||||
|
@ -190,7 +209,9 @@ public final class StringUtils {
|
||||||
return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff);
|
return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String capitalizeFirstAndDowncaseRest(final String s, final Locale locale) {
|
@Nonnull
|
||||||
|
public static String capitalizeFirstAndDowncaseRest(@Nonnull final String s,
|
||||||
|
@Nonnull final Locale locale) {
|
||||||
if (s.length() <= 1) {
|
if (s.length() <= 1) {
|
||||||
return s.toUpperCase(locale);
|
return s.toUpperCase(locale);
|
||||||
}
|
}
|
||||||
|
@ -206,12 +227,14 @@ public final class StringUtils {
|
||||||
return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff).toLowerCase(locale);
|
return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff).toLowerCase(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int[] EMPTY_CODEPOINTS = {};
|
@Nonnull
|
||||||
|
public static int[] toCodePointArray(@Nonnull final CharSequence charSequence) {
|
||||||
public static int[] toCodePointArray(final CharSequence charSequence) {
|
|
||||||
return toCodePointArray(charSequence, 0, charSequence.length());
|
return toCodePointArray(charSequence, 0, charSequence.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static final int[] EMPTY_CODEPOINTS = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a range of a string to an array of code points.
|
* Converts a range of a string to an array of code points.
|
||||||
* @param charSequence the source string.
|
* @param charSequence the source string.
|
||||||
|
@ -219,7 +242,8 @@ public final class StringUtils {
|
||||||
* @param endIndex the end index inside the string in java chars, exclusive.
|
* @param endIndex the end index inside the string in java chars, exclusive.
|
||||||
* @return a new array of code points. At most endIndex - startIndex, but possibly less.
|
* @return a new array of code points. At most endIndex - startIndex, but possibly less.
|
||||||
*/
|
*/
|
||||||
public static int[] toCodePointArray(final CharSequence charSequence,
|
@Nonnull
|
||||||
|
public static int[] toCodePointArray(@Nonnull final CharSequence charSequence,
|
||||||
final int startIndex, final int endIndex) {
|
final int startIndex, final int endIndex) {
|
||||||
final int length = charSequence.length();
|
final int length = charSequence.length();
|
||||||
if (length <= 0) {
|
if (length <= 0) {
|
||||||
|
@ -250,8 +274,8 @@ public final class StringUtils {
|
||||||
* @param downCase if this is true, code points will be downcased before being copied.
|
* @param downCase if this is true, code points will be downcased before being copied.
|
||||||
* @return the number of copied code points.
|
* @return the number of copied code points.
|
||||||
*/
|
*/
|
||||||
public static int copyCodePointsAndReturnCodePointCount(final int[] destination,
|
public static int copyCodePointsAndReturnCodePointCount(@Nonnull final int[] destination,
|
||||||
final CharSequence charSequence, final int startIndex, final int endIndex,
|
@Nonnull final CharSequence charSequence, final int startIndex, final int endIndex,
|
||||||
final boolean downCase) {
|
final boolean downCase) {
|
||||||
int destIndex = 0;
|
int destIndex = 0;
|
||||||
for (int index = startIndex; index < endIndex;
|
for (int index = startIndex; index < endIndex;
|
||||||
|
@ -265,7 +289,8 @@ public final class StringUtils {
|
||||||
return destIndex;
|
return destIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int[] toSortedCodePointArray(final String string) {
|
@Nonnull
|
||||||
|
public static int[] toSortedCodePointArray(@Nonnull final String string) {
|
||||||
final int[] codePoints = toCodePointArray(string);
|
final int[] codePoints = toCodePointArray(string);
|
||||||
Arrays.sort(codePoints);
|
Arrays.sort(codePoints);
|
||||||
return codePoints;
|
return codePoints;
|
||||||
|
@ -278,7 +303,9 @@ public final class StringUtils {
|
||||||
* shorter than the array length.
|
* shorter than the array length.
|
||||||
* @return a string constructed from the code point array.
|
* @return a string constructed from the code point array.
|
||||||
*/
|
*/
|
||||||
public static String getStringFromNullTerminatedCodePointArray(final int[] codePoints) {
|
@Nonnull
|
||||||
|
public static String getStringFromNullTerminatedCodePointArray(
|
||||||
|
@Nonnull final int[] codePoints) {
|
||||||
int stringLength = codePoints.length;
|
int stringLength = codePoints.length;
|
||||||
for (int i = 0; i < codePoints.length; i++) {
|
for (int i = 0; i < codePoints.length; i++) {
|
||||||
if (codePoints[i] == 0) {
|
if (codePoints[i] == 0) {
|
||||||
|
@ -290,7 +317,7 @@ public final class StringUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method assumes the text is not null. For the empty string, it returns CAPITALIZE_NONE.
|
// This method assumes the text is not null. For the empty string, it returns CAPITALIZE_NONE.
|
||||||
public static int getCapitalizationType(final String text) {
|
public static int getCapitalizationType(@Nonnull final String text) {
|
||||||
// If the first char is not uppercase, then the word is either all lower case or
|
// If the first char is not uppercase, then the word is either all lower case or
|
||||||
// camel case, and in either case we return CAPITALIZE_NONE.
|
// camel case, and in either case we return CAPITALIZE_NONE.
|
||||||
final int len = text.length();
|
final int len = text.length();
|
||||||
|
@ -326,7 +353,7 @@ public final class StringUtils {
|
||||||
return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
|
return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isIdenticalAfterUpcase(final String text) {
|
public static boolean isIdenticalAfterUpcase(@Nonnull final String text) {
|
||||||
final int length = text.length();
|
final int length = text.length();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < length) {
|
while (i < length) {
|
||||||
|
@ -339,7 +366,7 @@ public final class StringUtils {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isIdenticalAfterDowncase(final String text) {
|
public static boolean isIdenticalAfterDowncase(@Nonnull final String text) {
|
||||||
final int length = text.length();
|
final int length = text.length();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < length) {
|
while (i < length) {
|
||||||
|
@ -352,8 +379,8 @@ public final class StringUtils {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isIdenticalAfterCapitalizeEachWord(final String text,
|
public static boolean isIdenticalAfterCapitalizeEachWord(@Nonnull final String text,
|
||||||
final int[] sortedSeparators) {
|
@Nonnull final int[] sortedSeparators) {
|
||||||
boolean needsCapsNext = true;
|
boolean needsCapsNext = true;
|
||||||
final int len = text.length();
|
final int len = text.length();
|
||||||
for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
|
for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
|
||||||
|
@ -372,8 +399,9 @@ public final class StringUtils {
|
||||||
|
|
||||||
// TODO: like capitalizeFirst*, this does not work perfectly for Dutch because of the IJ digraph
|
// TODO: like capitalizeFirst*, this does not work perfectly for Dutch because of the IJ digraph
|
||||||
// which should be capitalized together in *some* cases.
|
// which should be capitalized together in *some* cases.
|
||||||
public static String capitalizeEachWord(final String text, final int[] sortedSeparators,
|
@Nonnull
|
||||||
final Locale locale) {
|
public static String capitalizeEachWord(@Nonnull final String text,
|
||||||
|
@Nonnull final int[] sortedSeparators, @Nonnull final Locale locale) {
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
boolean needsCapsNext = true;
|
boolean needsCapsNext = true;
|
||||||
final int len = text.length();
|
final int len = text.length();
|
||||||
|
@ -407,9 +435,11 @@ public final class StringUtils {
|
||||||
* TODO: This will return that "abc./def" and ".abc/def" look like URLs to keep down the
|
* TODO: This will return that "abc./def" and ".abc/def" look like URLs to keep down the
|
||||||
* code complexity, but ideally it should not. It's acceptable for now.
|
* code complexity, but ideally it should not. It's acceptable for now.
|
||||||
*/
|
*/
|
||||||
public static boolean lastPartLooksLikeURL(final CharSequence text) {
|
public static boolean lastPartLooksLikeURL(@Nonnull final CharSequence text) {
|
||||||
int i = text.length();
|
int i = text.length();
|
||||||
if (0 == i) return false;
|
if (0 == i) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int wCount = 0;
|
int wCount = 0;
|
||||||
int slashCount = 0;
|
int slashCount = 0;
|
||||||
boolean hasSlash = false;
|
boolean hasSlash = false;
|
||||||
|
@ -446,11 +476,17 @@ public final class StringUtils {
|
||||||
}
|
}
|
||||||
// End of the text run.
|
// End of the text run.
|
||||||
// If it starts with www and includes a period, then it looks like a URL.
|
// If it starts with www and includes a period, then it looks like a URL.
|
||||||
if (wCount >= 3 && hasPeriod) return true;
|
if (wCount >= 3 && hasPeriod) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// If it starts with a slash, and the code point before is whitespace, it looks like an URL.
|
// If it starts with a slash, and the code point before is whitespace, it looks like an URL.
|
||||||
if (1 == slashCount && (0 == i || Character.isWhitespace(codePoint))) return true;
|
if (1 == slashCount && (0 == i || Character.isWhitespace(codePoint))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// If it has both a period and a slash, it looks like an URL.
|
// If it has both a period and a slash, it looks like an URL.
|
||||||
if (hasPeriod && hasSlash) return true;
|
if (hasPeriod && hasSlash) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Otherwise, it doesn't look like an URL.
|
// Otherwise, it doesn't look like an URL.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -471,18 +507,24 @@ public final class StringUtils {
|
||||||
* @param text the text to examine.
|
* @param text the text to examine.
|
||||||
* @return whether we're inside a double quote.
|
* @return whether we're inside a double quote.
|
||||||
*/
|
*/
|
||||||
public static boolean isInsideDoubleQuoteOrAfterDigit(final CharSequence text) {
|
public static boolean isInsideDoubleQuoteOrAfterDigit(@Nonnull final CharSequence text) {
|
||||||
int i = text.length();
|
int i = text.length();
|
||||||
if (0 == i) return false;
|
if (0 == i) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int codePoint = Character.codePointBefore(text, i);
|
int codePoint = Character.codePointBefore(text, i);
|
||||||
if (Character.isDigit(codePoint)) return true;
|
if (Character.isDigit(codePoint)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
int prevCodePoint = 0;
|
int prevCodePoint = 0;
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
codePoint = Character.codePointBefore(text, i);
|
codePoint = Character.codePointBefore(text, i);
|
||||||
if (Constants.CODE_DOUBLE_QUOTE == codePoint) {
|
if (Constants.CODE_DOUBLE_QUOTE == codePoint) {
|
||||||
// If we see a double quote followed by whitespace, then that
|
// If we see a double quote followed by whitespace, then that
|
||||||
// was a closing quote.
|
// was a closing quote.
|
||||||
if (Character.isWhitespace(prevCodePoint)) return false;
|
if (Character.isWhitespace(prevCodePoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Character.isWhitespace(codePoint) && Constants.CODE_DOUBLE_QUOTE == prevCodePoint) {
|
if (Character.isWhitespace(codePoint) && Constants.CODE_DOUBLE_QUOTE == prevCodePoint) {
|
||||||
// If we see a double quote preceded by whitespace, then that
|
// If we see a double quote preceded by whitespace, then that
|
||||||
|
@ -497,7 +539,7 @@ public final class StringUtils {
|
||||||
return Constants.CODE_DOUBLE_QUOTE == codePoint;
|
return Constants.CODE_DOUBLE_QUOTE == codePoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isEmptyStringOrWhiteSpaces(final String s) {
|
public static boolean isEmptyStringOrWhiteSpaces(@Nonnull final String s) {
|
||||||
final int N = codePointCount(s);
|
final int N = codePointCount(s);
|
||||||
for (int i = 0; i < N; ++i) {
|
for (int i = 0; i < N; ++i) {
|
||||||
if (!Character.isWhitespace(s.codePointAt(i))) {
|
if (!Character.isWhitespace(s.codePointAt(i))) {
|
||||||
|
@ -508,12 +550,13 @@ public final class StringUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@UsedForTesting
|
@UsedForTesting
|
||||||
public static String byteArrayToHexString(final byte[] bytes) {
|
@Nonnull
|
||||||
|
public static String byteArrayToHexString(@Nullable final byte[] bytes) {
|
||||||
if (bytes == null || bytes.length == 0) {
|
if (bytes == null || bytes.length == 0) {
|
||||||
return EMPTY_STRING;
|
return EMPTY_STRING;
|
||||||
}
|
}
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
for (byte b : bytes) {
|
for (final byte b : bytes) {
|
||||||
sb.append(String.format("%02x", b & 0xff));
|
sb.append(String.format("%02x", b & 0xff));
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
@ -523,7 +566,8 @@ public final class StringUtils {
|
||||||
* Convert hex string to byte array. The string length must be an even number.
|
* Convert hex string to byte array. The string length must be an even number.
|
||||||
*/
|
*/
|
||||||
@UsedForTesting
|
@UsedForTesting
|
||||||
public static byte[] hexStringToByteArray(final String hexString) {
|
@Nullable
|
||||||
|
public static byte[] hexStringToByteArray(@Nullable final String hexString) {
|
||||||
if (isEmpty(hexString)) {
|
if (isEmpty(hexString)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -540,15 +584,20 @@ public final class StringUtils {
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toUpperCaseOfStringForLocale(final String text,
|
@Nullable
|
||||||
final boolean needsToUpperCase, final Locale locale) {
|
public static String toUpperCaseOfStringForLocale(@Nullable final String text,
|
||||||
if (text == null || !needsToUpperCase) return text;
|
final boolean needsToUpperCase, @Nonnull final Locale locale) {
|
||||||
|
if (text == null || !needsToUpperCase) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
return text.toUpperCase(locale);
|
return text.toUpperCase(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
|
public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
|
||||||
final Locale locale) {
|
@Nonnull final Locale locale) {
|
||||||
if (!Constants.isLetterCode(code) || !needsToUpperCase) return code;
|
if (!Constants.isLetterCode(code) || !needsToUpperCase) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
final String text = newSingleCodePointString(code);
|
final String text = newSingleCodePointString(code);
|
||||||
final String casedText = toUpperCaseOfStringForLocale(
|
final String casedText = toUpperCaseOfStringForLocale(
|
||||||
text, needsToUpperCase, locale);
|
text, needsToUpperCase, locale);
|
||||||
|
@ -556,7 +605,7 @@ public final class StringUtils {
|
||||||
? casedText.codePointAt(0) : Constants.CODE_UNSPECIFIED;
|
? casedText.codePointAt(0) : Constants.CODE_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getTrailingSingleQuotesCount(final CharSequence charSequence) {
|
public static int getTrailingSingleQuotesCount(@Nonnull final CharSequence charSequence) {
|
||||||
final int lastIndex = charSequence.length() - 1;
|
final int lastIndex = charSequence.length() - 1;
|
||||||
int i = lastIndex;
|
int i = lastIndex;
|
||||||
while (i >= 0 && charSequence.charAt(i) == Constants.CODE_SINGLE_QUOTE) {
|
while (i >= 0 && charSequence.charAt(i) == Constants.CODE_SINGLE_QUOTE) {
|
||||||
|
@ -565,20 +614,36 @@ public final class StringUtils {
|
||||||
return lastIndex - i;
|
return lastIndex - i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UsedForTesting
|
||||||
public static class Stringizer<E> {
|
public static class Stringizer<E> {
|
||||||
public String stringize(final E element) {
|
@Nonnull
|
||||||
return element != null ? element.toString() : "null";
|
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||||
|
|
||||||
|
@UsedForTesting
|
||||||
|
@Nonnull
|
||||||
|
public String stringize(@Nullable final E element) {
|
||||||
|
if (element == null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
return element.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String join(final E[] array) {
|
@UsedForTesting
|
||||||
|
@Nonnull
|
||||||
|
public final String join(@Nullable final E[] array) {
|
||||||
return joinStringArray(toStringArray(array), null /* delimiter */);
|
return joinStringArray(toStringArray(array), null /* delimiter */);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String join(final E[] array, final String delimiter) {
|
@UsedForTesting
|
||||||
|
public final String join(@Nullable final E[] array, @Nullable final String delimiter) {
|
||||||
return joinStringArray(toStringArray(array), delimiter);
|
return joinStringArray(toStringArray(array), delimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String[] toStringArray(final E[] array) {
|
@Nonnull
|
||||||
|
protected String[] toStringArray(@Nullable final E[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
return EMPTY_STRING_ARRAY;
|
||||||
|
}
|
||||||
final String[] stringArray = new String[array.length];
|
final String[] stringArray = new String[array.length];
|
||||||
for (int index = 0; index < array.length; index++) {
|
for (int index = 0; index < array.length; index++) {
|
||||||
stringArray[index] = stringize(array[index]);
|
stringArray[index] = stringize(array[index]);
|
||||||
|
@ -586,10 +651,9 @@ public final class StringUtils {
|
||||||
return stringArray;
|
return stringArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String joinStringArray(final String[] stringArray, final String delimiter) {
|
@Nonnull
|
||||||
if (stringArray == null) {
|
protected String joinStringArray(@Nonnull final String[] stringArray,
|
||||||
return "null";
|
@Nullable final String delimiter) {
|
||||||
}
|
|
||||||
if (delimiter == null) {
|
if (delimiter == null) {
|
||||||
return Arrays.toString(stringArray);
|
return Arrays.toString(stringArray);
|
||||||
}
|
}
|
||||||
|
@ -607,7 +671,7 @@ public final class StringUtils {
|
||||||
* @param text the text to be examined.
|
* @param text the text to be examined.
|
||||||
* @return {@code true} if the last composed word contains line-breaking separator.
|
* @return {@code true} if the last composed word contains line-breaking separator.
|
||||||
*/
|
*/
|
||||||
public static boolean hasLineBreakCharacter(final String text) {
|
public static boolean hasLineBreakCharacter(@Nullable final String text) {
|
||||||
if (isEmpty(text)) {
|
if (isEmpty(text)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ import com.android.inputmethod.latin.common.StringUtils;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class builds an actual keyboard for unit test.
|
* This class builds an actual keyboard for unit test.
|
||||||
*
|
*
|
||||||
|
@ -98,9 +101,13 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String stringize(final MoreKeySpec spec) {
|
public String stringize(final MoreKeySpec spec) {
|
||||||
|
if (spec == null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode);
|
return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
static String toString(final String label, final int iconId, final String outputText,
|
static String toString(final String label, final int iconId, final String outputText,
|
||||||
final int code) {
|
final int code) {
|
||||||
final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED)
|
final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED)
|
||||||
|
@ -125,7 +132,7 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
|
||||||
static final KeyStringizer STRINGIZER = new KeyStringizer();
|
static final KeyStringizer STRINGIZER = new KeyStringizer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String stringize(final Key key) {
|
public String stringize(@Nullable final Key key) {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
return "NULL";
|
return "NULL";
|
||||||
}
|
}
|
||||||
|
@ -150,7 +157,8 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
|
||||||
* @param key the key to be converted to string.
|
* @param key the key to be converted to string.
|
||||||
* @return the human readable representation of <code>key</code>.
|
* @return the human readable representation of <code>key</code>.
|
||||||
*/
|
*/
|
||||||
public static String toString(final Key key) {
|
@Nonnull
|
||||||
|
public static String toString(@Nullable final Key key) {
|
||||||
return KeyStringizer.STRINGIZER.stringize(key);
|
return KeyStringizer.STRINGIZER.stringize(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +167,8 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
|
||||||
* @param keys the keyboard row to be converted to string.
|
* @param keys the keyboard row to be converted to string.
|
||||||
* @return the human readable representation of <code>keys</code>.
|
* @return the human readable representation of <code>keys</code>.
|
||||||
*/
|
*/
|
||||||
public static String toString(final Key[] keys) {
|
@Nonnull
|
||||||
|
public static String toString(@Nullable final Key[] keys) {
|
||||||
return KeyStringizer.STRINGIZER.join(keys);
|
return KeyStringizer.STRINGIZER.join(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +177,7 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
|
||||||
static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer();
|
static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String stringize(final Key[] keyArray) {
|
public String stringize(@Nullable final Key[] keyArray) {
|
||||||
return KeyStringizer.STRINGIZER.join(keyArray);
|
return KeyStringizer.STRINGIZER.join(keyArray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +187,8 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> {
|
||||||
* @param rows the keyboard to be converted to string.
|
* @param rows the keyboard to be converted to string.
|
||||||
* @return the human readable representation of <code>rows</code>.
|
* @return the human readable representation of <code>rows</code>.
|
||||||
*/
|
*/
|
||||||
public static String toString(final Key[][] rows) {
|
@Nonnull
|
||||||
|
public static String toString(@Nullable final Key[][] rows) {
|
||||||
return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */);
|
return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue