Skip to content

Commit

Permalink
Refactor & tidy up uses of Strings class (#90672)
Browse files Browse the repository at this point in the history
Refactor & tidy up uses of Strings class
Add some unit tests to string methods
  • Loading branch information
thecoop committed Oct 13, 2022
1 parent 1a3032b commit 9cb299b
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 218 deletions.
193 changes: 60 additions & 133 deletions server/src/main/java/org/elasticsearch/common/Strings.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
Expand All @@ -40,72 +41,16 @@ public class Strings {

public static final String[] EMPTY_ARRAY = new String[0];

public static void spaceify(int spaces, String from, StringBuilder to) throws Exception {
public static void spaceify(int spaces, String from, StringBuilder to) throws IOException {
char[] spaceChars = new char[spaces];
Arrays.fill(spaceChars, ' ');

try (BufferedReader reader = new BufferedReader(new StringReader(from))) {
String line;
while ((line = reader.readLine()) != null) {
for (int i = 0; i < spaces; i++) {
to.append(' ');
}
to.append(line).append('\n');
}
}
}

/**
* Splits a backslash escaped string on the separator.
* <p>
* Current backslash escaping supported:
* <br> \n \t \r \b \f are escaped the same as a Java String
* <br> Other characters following a backslash are produced verbatim (\c =&gt; c)
*
* @param s the string to split
* @param separator the separator to split on
* @param decode decode backslash escaping
*/
public static List<String> splitSmart(String s, String separator, boolean decode) {
ArrayList<String> lst = new ArrayList<>(2);
StringBuilder sb = new StringBuilder();
int pos = 0, end = s.length();
while (pos < end) {
if (s.startsWith(separator, pos)) {
if (sb.length() > 0) {
lst.add(sb.toString());
sb = new StringBuilder();
}
pos += separator.length();
continue;
}

char ch = s.charAt(pos++);
if (ch == '\\') {
if (decode == false) {
sb.append(ch);
}
if (pos >= end) {
break; // ERROR, or let it go?
}
ch = s.charAt(pos++);
if (decode) {
ch = switch (ch) {
case 'n' -> '\n';
case 't' -> '\t';
case 'r' -> '\r';
case 'b' -> '\b';
case 'f' -> '\f';
default -> ch;
};
}
to.append(spaceChars).append(line).append('\n');
}

sb.append(ch);
}

if (sb.length() > 0) {
lst.add(sb.toString());
}

return lst;
}

// ---------------------------------------------------------------------
Expand All @@ -127,7 +72,7 @@ public static List<String> splitSmart(String s, String separator, boolean decode
* @see #hasText(String)
*/
public static boolean hasLength(CharSequence str) {
return (str != null && str.length() > 0);
return (str != null && str.isEmpty() == false);
}

/**
Expand Down Expand Up @@ -192,13 +137,7 @@ public static boolean hasText(CharSequence str) {
if (hasLength(str) == false) {
return false;
}
int strLen = str.length();
for (int i = 0; i < strLen; i++) {
if (Character.isWhitespace(str.charAt(i)) == false) {
return true;
}
}
return false;
return str.chars().anyMatch(c -> Character.isWhitespace(c) == false);
}

/**
Expand Down Expand Up @@ -226,11 +165,11 @@ public static String trimLeadingCharacter(String str, char leadingCharacter) {
if (hasLength(str) == false) {
return str;
}
StringBuilder sb = new StringBuilder(str);
while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) {
sb.deleteCharAt(0);
int i = 0;
while (i < str.length() && str.charAt(i) == leadingCharacter) {
i++;
}
return sb.toString();
return str.substring(i);
}

/**
Expand Down Expand Up @@ -270,7 +209,7 @@ public static String replace(String inString, String oldPattern, String newPatte
// the index of an occurrence we've found, or -1
int patLen = oldPattern.length();
while (index >= 0) {
sb.append(inString.substring(pos, index));
sb.append(inString, pos, index);
sb.append(newPattern);
pos = index + patLen;
index = inString.indexOf(oldPattern, pos);
Expand All @@ -289,17 +228,29 @@ public static String replace(String inString, String oldPattern, String newPatte
* @return the resulting String
*/
public static String deleteAny(String inString, String charsToDelete) {
return inString != null ? deleteAny((CharSequence) inString, charsToDelete).toString() : null;
}

/**
* Delete any character in a given CharSequence.
*
* @param inString the original CharSequence
* @param charsToDelete a set of characters to delete.
* E.g. "az\n" will delete 'a's, 'z's and new lines.
* @return the resulting CharSequence
*/
public static CharSequence deleteAny(CharSequence inString, String charsToDelete) {
if (hasLength(inString) == false || hasLength(charsToDelete) == false) {
return inString;
}
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder(inString.length());
for (int i = 0; i < inString.length(); i++) {
char c = inString.charAt(i);
if (charsToDelete.indexOf(c) == -1) {
sb.append(c);
}
}
return sb.toString();
return sb;
}

// ---------------------------------------------------------------------
Expand All @@ -322,37 +273,24 @@ private static String changeFirstCharacterCase(String str, boolean capitalize) {
if (str == null || str.length() == 0) {
return str;
}
StringBuilder sb = new StringBuilder(str.length());
if (capitalize) {
sb.append(Character.toUpperCase(str.charAt(0)));
} else {
sb.append(Character.toLowerCase(str.charAt(0)));
char newChar = capitalize ? Character.toUpperCase(str.charAt(0)) : Character.toLowerCase(str.charAt(0));
if (newChar == str.charAt(0)) {
return str; // nothing changed
}
sb.append(str.substring(1));
return sb.toString();

return newChar + str.substring(1);
}

public static final String INVALID_FILENAME_CHARS = "["
+ Stream.of('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',').map(c -> "'" + c + "'").collect(Collectors.joining(","))
+ "]";
public static final String INVALID_FILENAME_CHARS = Stream.of('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',')
.map(c -> "'" + c + "'")
.collect(Collectors.joining(",", "[", "]"));

public static boolean validFileName(String fileName) {
for (int i = 0; i < fileName.length(); i++) {
if (isInvalidFileNameCharacter(fileName.charAt(i))) {
return false;
}
}
return true;
return fileName.chars().noneMatch(c -> isInvalidFileNameCharacter((char) c));
}

public static boolean validFileNameExcludingAstrix(String fileName) {
for (int i = 0; i < fileName.length(); i++) {
char c = fileName.charAt(i);
if (c != '*' && isInvalidFileNameCharacter(c)) {
return false;
}
}
return true;
return fileName.chars().noneMatch(c -> c != '*' && isInvalidFileNameCharacter((char) c));
}

private static boolean isInvalidFileNameCharacter(char c) {
Expand All @@ -374,7 +312,7 @@ public static String[] toStringArray(Collection<String> collection) {
if (collection == null) {
return null;
}
return collection.toArray(new String[collection.size()]);
return collection.toArray(String[]::new);
}

/**
Expand Down Expand Up @@ -532,21 +470,31 @@ public static String[] delimitedListToStringArray(String str, String delimiter,
if (delimiter == null) {
return new String[] { str };
}
List<String> result = new ArrayList<>();
if ("".equals(delimiter)) {
List<String> result;
if (delimiter.isEmpty()) {
// split on every character
result = new ArrayList<>(str.length());
if (charsToDelete == null) {
charsToDelete = "";
}
for (int i = 0; i < str.length(); i++) {
result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
if (charsToDelete.indexOf(str.charAt(i)) == -1) {
result.add(Character.toString(str.charAt(i)));
} else {
result.add("");
}
}
} else {
result = new ArrayList<>();
int pos = 0;
int delPos;
while ((delPos = str.indexOf(delimiter, pos)) != -1) {
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
result.add(deleteAny(str.subSequence(pos, delPos), charsToDelete).toString());
pos = delPos + delimiter.length();
}
if (str.length() > 0 && pos <= str.length()) {
// Add rest of String, but not in case of empty input.
result.add(deleteAny(str.substring(pos), charsToDelete));
result.add(deleteAny(str.subSequence(pos, str.length()), charsToDelete).toString());
}
}
return toStringArray(result);
Expand All @@ -570,10 +518,8 @@ public static String[] commaDelimitedListToStringArray(String str) {
* @return a Set of String entries in the list
*/
public static Set<String> commaDelimitedListToSet(String str) {
Set<String> set = new TreeSet<>();
String[] tokens = commaDelimitedListToStringArray(str);
set.addAll(Arrays.asList(tokens));
return set;
return new TreeSet<>(Arrays.asList(tokens));
}

/**
Expand Down Expand Up @@ -939,38 +885,19 @@ public static boolean isNullOrBlank(@Nullable String s) {
return s == null || s.isBlank();
}

public static String coalesceToEmpty(@Nullable String s) {
return s == null ? "" : s;
}

public static String padStart(String s, int minimumLength, char c) {
if (s == null) {
throw new NullPointerException("s");
}
Objects.requireNonNull(s, "s");
if (s.length() >= minimumLength) {
return s;
} else {
StringBuilder sb = new StringBuilder(minimumLength);
for (int i = s.length(); i < minimumLength; i++) {
sb.append(c);
}

sb.append(s);
return sb.toString();
return Character.toString(c).repeat(minimumLength - s.length()) + s;
}
}

public static String toLowercaseAscii(String in) {
StringBuilder out = new StringBuilder();
Iterator<Integer> iter = in.codePoints().iterator();
while (iter.hasNext()) {
int codepoint = iter.next();
if (codepoint > 128) {
out.appendCodePoint(codepoint);
} else {
out.appendCodePoint(Character.toLowerCase(codepoint));
}
}
return out.toString();
return in.codePoints()
.map(cp -> cp <= 128 ? Character.toLowerCase(cp) : cp)
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
}
}

0 comments on commit 9cb299b

Please sign in to comment.