package com.supaham.commons.utils; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.CharMatcher; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import org.jetbrains.annotations.Contract; import java.util.Iterator; import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * Utility methods for working with {@link String} instances. This class contains methods such as * {@link #isASCII(String)}, {@link #checkNotNullOrEmpty(String)}, and more. * * @since 0.1 */ public final class StringUtils extends org.apache.commons.lang.StringUtils { private StringUtils() { } /** * Checks if a {@link String} contains only ASCII characters. * * @param string the String to check * * @return whether the {@code string} is ASCII only */ public static boolean isASCII(@Nonnull String string) { return CharMatcher.ASCII.matchesAllOf(checkNotNull(string, "string cannot be null.")); } /** * Separates multiple {@link String}s with an equal amount of spaces between all provided * Strings. * <p/> * E.g.: * <pre> * StringUtils.formatWhitespace(0, "") = {@link IllegalArgumentException} * StringUtils.formatWhitespace(0, null) = {@link NullPointerException} * </pre> * * @param length length of the String * @param args Strings to center * * @return the formatted string */ @Nonnull public static String formatWhitespace(int length, @Nullable String... args) { if (length == 0 || args == null || args.length == 0) { return ""; } // Calculate the total length of all the provided arguments combined, and the actual argument // length, removing any empty or null provided strings. args = ArrayUtils.removeEmptyStrings(args, true); int totalLength = ArrayUtils.getTotalStringsLength(args); checkArgument(totalLength <= length, "args length can not be larger than the length."); // This supports only providing one arg, currently redundant, but I thought it better failing // safely. if (args.length <= 1) { return args[0]; } StringBuilder builder = new StringBuilder(length); // Spaces to add, I think this might need some more precision as it does not account for oddness int spaces = (int) Math.ceil((length - totalLength) / ((double) args.length - 1)); String whitespaces = repeat(" ", spaces); for (int i = 0; i < args.length; i++) { String arg = args[i]; builder.append(arg); // Check that this isn't the last argument, if that's the case, add the whitespaces. if ((i + 1) < args.length) { builder.append(whitespaces); } } return builder.toString(); } /** * Normalizes a string. The following characters are converted to an underscore '_': * <ul> * <li><b>hyphen</b> '-'</li> * <li><b>space</b> ' '</li> * </ul> * * @param str String to normalize * * @return the normalized string */ @Contract("null -> null") public static String normalizeString(@Nullable String str) { if (str == null) { return null; } return str.replaceAll("[- ]+", "_").toLowerCase(); } /** * Checks if a String is null or empty, if it is, an exception is thrown. * <p/> * E.g.: * <pre> * StringUtils.checkNotNullOrEmpty(null) = {@link NullPointerException} * StringUtils.checkNotNullOrEmpty("") = {@link IllegalArgumentException} * StringUtils.checkNotNullOrEmpty("Hello") = "Hello" * </pre> * * @param string string to check * * @return the same exact {@code string} returned for chaining. * * @throws NullPointerException thrown if the {@code string} is null * @throws IllegalArgumentException thrown if the {@code string} is empty * @see #checkNotNullOrEmpty(String, String) */ public static String checkNotNullOrEmpty(@Nullable String string) throws NullPointerException, IllegalArgumentException { return checkNotNullOrEmpty(string, null); } /** * Checks if a String is null or empty, if it is, an exception is thrown. * <p/> * E.g.: * <pre> * StringUtils.checkNotNullOrEmpty(null, "mystring") = {@link NullPointerException} * StringUtils.checkNotNullOrEmpty("", "mystring") = {@link IllegalArgumentException} * StringUtils.checkNotNullOrEmpty("Hello", "mystring") = "Hello" * </pre> * * @param string string to check * @param desc name of the {@code string} used to detail the thrown exception. If null, it is * then set to "String". * * @return the same exact {@code string} returned for chaining. * * @throws NullPointerException thrown if the {@code string} is null * @throws IllegalArgumentException thrown if the {@code string} is empty */ public static String checkNotNullOrEmpty(@Nullable String string, @Nullable String desc) throws NullPointerException, IllegalArgumentException { if (desc == null) { desc = "string"; } checkNotNull(string, desc + " cannot be null."); checkArgument(!string.isEmpty(), desc + " cannot be empty."); return string; } /** * This only supports regular nouns such as school, cry, crash, not irregular nouns such as, * woman, child, mouse, leaf. * * @param count integer to decide whether we need to append plural suffix * @param singularTerm singular term to create the plural suffix for * @param uppercase whether the plural suffix should be uppercase * * @return plural term if count is not 1, otherwise the same {@code singularTerm} is returned */ public static String appendIfPlural(int count, String singularTerm, boolean uppercase) { if (count == 1) { return singularTerm; } String lcase = singularTerm.toLowerCase(); if (lcase.matches(".*([sxz]|ch|sh)$")) { return singularTerm + (uppercase ? "ES" : "es"); } else if (lcase.endsWith("y")) { return singularTerm.substring(0, singularTerm.length() - 1) + (uppercase ? "IES" : "ies"); } else { return singularTerm + "s"; } } /** * Transforms an {@link Iterable} using a {@link Function} and string-joins it using a * {@link Joiner}. Useful if the {@code part}'s {@code toString()} method outputs something else. * * @param joiner joiner to call {@link Joiner#join(Iterator)} with * @param parts parts to join, argument passed to {@link Joiner#join(Iterator)} * @param function function that transforms the parts into string * * @return the same exact {@code string} returned for chaining. * * @throws NullPointerException thrown if any of the parameters are null * @see com.supaham.commons.Joiner */ public static <T> String joinerFunction(@Nonnull Joiner joiner, @Nonnull Iterable<T> parts, @Nonnull Function<T, String> function) { Preconditions.checkNotNull(joiner, "joiner cannot be null."); Preconditions.checkNotNull(parts, "parts cannot be null."); Preconditions.checkNotNull(function, "function cannot be null."); return joiner.join(Iterables.transform(parts, function)); } public static Optional<Boolean> parseBoolean(String string) { if (string == null) { return Optional.empty(); } string = string.toLowerCase(); return "false".equals(string) || "true".equals(string) ? Optional.of(Boolean.parseBoolean(string)) : Optional.empty(); } public static boolean isStringOrNumber(Object o) { return o instanceof String || o instanceof Number; } }