All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.common.Strings Maven / Gradle / Ivy

There is a newer version: 8.13.2
Show newest version
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.common;

import org.apache.lucene.util.BytesRefBuilder;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.function.Supplier;

import static java.util.Collections.unmodifiableSet;
import static org.elasticsearch.common.util.set.Sets.newHashSet;

public class Strings {

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

    public static void spaceify(int spaces, String from, StringBuilder to) throws Exception {
        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.
     * 

* Current backslash escaping supported: *
\n \t \r \b \f are escaped the same as a Java String *
Other characters following a backslash are produced verbatim (\c => c) * * @param s the string to split * @param separator the separator to split on * @param decode decode backslash escaping */ public static List splitSmart(String s, String separator, boolean decode) { ArrayList 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) sb.append(ch); if (pos >= end) break; // ERROR, or let it go? ch = s.charAt(pos++); if (decode) { switch (ch) { case 'n': ch = '\n'; break; case 't': ch = '\t'; break; case 'r': ch = '\r'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; } } } sb.append(ch); } if (sb.length() > 0) { lst.add(sb.toString()); } return lst; } //--------------------------------------------------------------------- // General convenience methods for working with Strings //--------------------------------------------------------------------- /** * Check that the given CharSequence is neither null nor of length 0. * Note: Will return true for a CharSequence that purely consists of whitespace. *

     * StringUtils.hasLength(null) = false
     * StringUtils.hasLength("") = false
     * StringUtils.hasLength(" ") = true
     * StringUtils.hasLength("Hello") = true
     * 
* * @param str the CharSequence to check (may be null) * @return true if the CharSequence is not null and has length * @see #hasText(String) */ public static boolean hasLength(CharSequence str) { return (str != null && str.length() > 0); } /** * Check that the given BytesReference is neither null nor of length 0 * Note: Will return true for a BytesReference that purely consists of whitespace. * * @param bytesReference the BytesReference to check (may be null) * @return true if the BytesReference is not null and has length * @see #hasLength(CharSequence) */ public static boolean hasLength(BytesReference bytesReference) { return (bytesReference != null && bytesReference.length() > 0); } /** * Check that the given String is neither null nor of length 0. * Note: Will return true for a String that purely consists of whitespace. * * @param str the String to check (may be null) * @return true if the String is not null and has length * @see #hasLength(CharSequence) */ public static boolean hasLength(String str) { return hasLength((CharSequence) str); } /** * Check that the given CharSequence is either null or of length 0. * Note: Will return false for a CharSequence that purely consists of whitespace. *
     * StringUtils.isEmpty(null) = true
     * StringUtils.isEmpty("") = true
     * StringUtils.isEmpty(" ") = false
     * StringUtils.isEmpty("Hello") = false
     * 
* * @param str the CharSequence to check (may be null) * @return true if the CharSequence is either null or has a zero length */ public static boolean isEmpty(CharSequence str) { return !hasLength(str); } /** * Check whether the given CharSequence has actual text. * More specifically, returns true if the string not null, * its length is greater than 0, and it contains at least one non-whitespace character. *
     * StringUtils.hasText(null) = false
     * StringUtils.hasText("") = false
     * StringUtils.hasText(" ") = false
     * StringUtils.hasText("12345") = true
     * StringUtils.hasText(" 12345 ") = true
     * 
* * @param str the CharSequence to check (may be null) * @return true if the CharSequence is not null, * its length is greater than 0, and it does not contain whitespace only * @see java.lang.Character#isWhitespace */ public static boolean hasText(CharSequence str) { if (!hasLength(str)) { return false; } int strLen = str.length(); for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(str.charAt(i))) { return true; } } return false; } /** * Check whether the given String has actual text. * More specifically, returns true if the string not null, * its length is greater than 0, and it contains at least one non-whitespace character. * * @param str the String to check (may be null) * @return true if the String is not null, its length is * greater than 0, and it does not contain whitespace only * @see #hasText(CharSequence) */ public static boolean hasText(String str) { return hasText((CharSequence) str); } /** * Trim all occurrences of the supplied leading character from the given String. * * @param str the String to check * @param leadingCharacter the leading character to be trimmed * @return the trimmed String */ public static String trimLeadingCharacter(String str, char leadingCharacter) { if (!hasLength(str)) { return str; } StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) { sb.deleteCharAt(0); } return sb.toString(); } /** * Test whether the given string matches the given substring * at the given index. * * @param str the original string (or StringBuilder) * @param index the index in the original string to start matching against * @param substring the substring to match at the given index */ public static boolean substringMatch(CharSequence str, int index, CharSequence substring) { for (int j = 0; j < substring.length(); j++) { int i = index + j; if (i >= str.length() || str.charAt(i) != substring.charAt(j)) { return false; } } return true; } /** * Replace all occurrences of a substring within a string with * another string. * * @param inString String to examine * @param oldPattern String to replace * @param newPattern String to insert * @return a String with the replacements */ public static String replace(String inString, String oldPattern, String newPattern) { if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { return inString; } StringBuilder sb = new StringBuilder(); int pos = 0; // our position in the old string int index = inString.indexOf(oldPattern); // 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(newPattern); pos = index + patLen; index = inString.indexOf(oldPattern, pos); } sb.append(inString.substring(pos)); // remember to append any characters to the right of a match return sb.toString(); } /** * Delete all occurrences of the given substring. * * @param inString the original String * @param pattern the pattern to delete all occurrences of * @return the resulting String */ public static String delete(String inString, String pattern) { return replace(inString, pattern, ""); } /** * Delete any character in a given String. * * @param inString the original String * @param charsToDelete a set of characters to delete. * E.g. "az\n" will delete 'a's, 'z's and new lines. * @return the resulting String */ public static String deleteAny(String inString, String charsToDelete) { if (!hasLength(inString) || !hasLength(charsToDelete)) { return inString; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < inString.length(); i++) { char c = inString.charAt(i); if (charsToDelete.indexOf(c) == -1) { sb.append(c); } } return sb.toString(); } //--------------------------------------------------------------------- // Convenience methods for working with formatted Strings //--------------------------------------------------------------------- /** * Quote the given String with single quotes. * * @param str the input String (e.g. "myString") * @return the quoted String (e.g. "'myString'"), * or null if the input was null */ public static String quote(String str) { return (str != null ? "'" + str + "'" : null); } /** * Capitalize a String, changing the first letter to * upper case as per {@link Character#toUpperCase(char)}. * No other letters are changed. * * @param str the String to capitalize, may be null * @return the capitalized String, null if null */ public static String capitalize(String str) { return changeFirstCharacterCase(str, true); } 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))); } sb.append(str.substring(1)); return sb.toString(); } public static final Set INVALID_FILENAME_CHARS = unmodifiableSet( newHashSet('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',')); public static boolean validFileName(String fileName) { for (int i = 0; i < fileName.length(); i++) { char c = fileName.charAt(i); if (INVALID_FILENAME_CHARS.contains(c)) { return false; } } return true; } public static boolean validFileNameExcludingAstrix(String fileName) { for (int i = 0; i < fileName.length(); i++) { char c = fileName.charAt(i); if (c != '*' && INVALID_FILENAME_CHARS.contains(c)) { return false; } } return true; } /** * Copy the given Collection into a String array. * The Collection must contain String elements only. * * @param collection the Collection to copy * @return the String array (null if the passed-in * Collection was null) */ public static String[] toStringArray(Collection collection) { if (collection == null) { return null; } return collection.toArray(new String[collection.size()]); } /** * Tokenize the specified string by commas to a set, trimming whitespace and ignoring empty tokens. * * @param s the string to tokenize * @return the set of tokens */ public static Set tokenizeByCommaToSet(final String s) { if (s == null) return Collections.emptySet(); return tokenizeToCollection(s, ",", HashSet::new); } /** * Split the specified string by commas to an array. * * @param s the string to split * @return the array of split values * @see String#split(String) */ public static String[] splitStringByCommaToArray(final String s) { if (s == null || s.isEmpty()) return Strings.EMPTY_ARRAY; else return s.split(","); } /** * Split a String at the first occurrence of the delimiter. * Does not include the delimiter in the result. * * @param toSplit the string to split * @param delimiter to split the string up with * @return a two element array with index 0 being before the delimiter, and * index 1 being after the delimiter (neither element includes the delimiter); * or null if the delimiter wasn't found in the given input String */ public static String[] split(String toSplit, String delimiter) { if (!hasLength(toSplit) || !hasLength(delimiter)) { return null; } int offset = toSplit.indexOf(delimiter); if (offset < 0) { return null; } String beforeDelimiter = toSplit.substring(0, offset); String afterDelimiter = toSplit.substring(offset + delimiter.length()); return new String[]{beforeDelimiter, afterDelimiter}; } /** * Tokenize the given String into a String array via a StringTokenizer. * Trims tokens and omits empty tokens. *

The given delimiters string is supposed to consist of any number of * delimiter characters. Each of those characters can be used to separate * tokens. A delimiter is always a single character; for multi-character * delimiters, consider using delimitedListToStringArray * * @param s the String to tokenize * @param delimiters the delimiter characters, assembled as String * (each of those characters is individually considered as delimiter). * @return an array of the tokens * @see java.util.StringTokenizer * @see java.lang.String#trim() * @see #delimitedListToStringArray */ public static String[] tokenizeToStringArray(final String s, final String delimiters) { if (s == null) { return EMPTY_ARRAY; } return toStringArray(tokenizeToCollection(s, delimiters, ArrayList::new)); } /** * Tokenizes the specified string to a collection using the specified delimiters as the token delimiters. This method trims whitespace * from tokens and ignores empty tokens. * * @param s the string to tokenize. * @param delimiters the token delimiters * @param supplier a collection supplier * @param the type of the collection * @return the tokens * @see java.util.StringTokenizer */ private static > T tokenizeToCollection( final String s, final String delimiters, final Supplier supplier) { if (s == null) { return null; } final StringTokenizer tokenizer = new StringTokenizer(s, delimiters); final T tokens = supplier.get(); while (tokenizer.hasMoreTokens()) { final String token = tokenizer.nextToken().trim(); if (token.length() > 0) { tokens.add(token); } } return tokens; } /** * Take a String which is a delimited list and convert it to a String array. *

A single delimiter can consists of more than one character: It will still * be considered as single delimiter string, rather than as bunch of potential * delimiter characters - in contrast to tokenizeToStringArray. * * @param str the input String * @param delimiter the delimiter between elements (this is a single delimiter, * rather than a bunch individual delimiter characters) * @return an array of the tokens in the list * @see #tokenizeToStringArray */ public static String[] delimitedListToStringArray(String str, String delimiter) { return delimitedListToStringArray(str, delimiter, null); } /** * Take a String which is a delimited list and convert it to a String array. *

A single delimiter can consists of more than one character: It will still * be considered as single delimiter string, rather than as bunch of potential * delimiter characters - in contrast to tokenizeToStringArray. * * @param str the input String * @param delimiter the delimiter between elements (this is a single delimiter, * rather than a bunch individual delimiter characters) * @param charsToDelete a set of characters to delete. Useful for deleting unwanted * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String. * @return an array of the tokens in the list * @see #tokenizeToStringArray */ public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { if (str == null) { return EMPTY_ARRAY; } if (delimiter == null) { return new String[]{str}; } List result = new ArrayList<>(); if ("".equals(delimiter)) { for (int i = 0; i < str.length(); i++) { result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); } } else { int pos = 0; int delPos; while ((delPos = str.indexOf(delimiter, pos)) != -1) { result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); 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)); } } return toStringArray(result); } /** * Convert a CSV list into an array of Strings. * * @param str the input String * @return an array of Strings, or the empty array in case of empty input */ public static String[] commaDelimitedListToStringArray(String str) { return delimitedListToStringArray(str, ","); } /** * Convenience method to convert a CSV string list to a set. * Note that this will suppress duplicates. * * @param str the input String * @return a Set of String entries in the list */ public static Set commaDelimitedListToSet(String str) { Set set = new TreeSet<>(); String[] tokens = commaDelimitedListToStringArray(str); set.addAll(Arrays.asList(tokens)); return set; } /** * Convenience method to return a Collection as a delimited (e.g. CSV) * String. E.g. useful for toString() implementations. * * @param coll the Collection to display * @param delim the delimiter to use (probably a ",") * @param prefix the String to start each element with * @param suffix the String to end each element with * @return the delimited String */ public static String collectionToDelimitedString(Iterable coll, String delim, String prefix, String suffix) { StringBuilder sb = new StringBuilder(); collectionToDelimitedString(coll, delim, prefix, suffix, sb); return sb.toString(); } public static void collectionToDelimitedString(Iterable coll, String delim, String prefix, String suffix, StringBuilder sb) { Iterator it = coll.iterator(); while (it.hasNext()) { sb.append(prefix).append(it.next()).append(suffix); if (it.hasNext()) { sb.append(delim); } } } /** * Convenience method to return a Collection as a delimited (e.g. CSV) * String. E.g. useful for toString() implementations. * * @param coll the Collection to display * @param delim the delimiter to use (probably a ",") * @return the delimited String */ public static String collectionToDelimitedString(Iterable coll, String delim) { return collectionToDelimitedString(coll, delim, "", ""); } /** * Convenience method to return a Collection as a CSV String. * E.g. useful for toString() implementations. * * @param coll the Collection to display * @return the delimited String */ public static String collectionToCommaDelimitedString(Iterable coll) { return collectionToDelimitedString(coll, ","); } /** * Convenience method to return a String array as a delimited (e.g. CSV) * String. E.g. useful for toString() implementations. * * @param arr the array to display * @param delim the delimiter to use (probably a ",") * @return the delimited String */ public static String arrayToDelimitedString(Object[] arr, String delim) { StringBuilder sb = new StringBuilder(); arrayToDelimitedString(arr, delim, sb); return sb.toString(); } public static void arrayToDelimitedString(Object[] arr, String delim, StringBuilder sb) { if (isEmpty(arr)) { return; } for (int i = 0; i < arr.length; i++) { if (i > 0) { sb.append(delim); } sb.append(arr[i]); } } /** * Convenience method to return a String array as a CSV String. * E.g. useful for toString() implementations. * * @param arr the array to display * @return the delimited String */ public static String arrayToCommaDelimitedString(Object[] arr) { return arrayToDelimitedString(arr, ","); } /** * Format the double value with a single decimal points, trimming trailing '.0'. */ public static String format1Decimals(double value, String suffix) { String p = String.valueOf(value); int ix = p.indexOf('.') + 1; int ex = p.indexOf('E'); char fraction = p.charAt(ix); if (fraction == '0') { if (ex != -1) { return p.substring(0, ix - 1) + p.substring(ex) + suffix; } else { return p.substring(0, ix - 1) + suffix; } } else { if (ex != -1) { return p.substring(0, ix) + fraction + p.substring(ex) + suffix; } else { return p.substring(0, ix) + fraction + suffix; } } } /** * Determine whether the given array is empty: * i.e. null or of zero length. * * @param array the array to check */ private static boolean isEmpty(Object[] array) { return (array == null || array.length == 0); } private Strings() { } public static byte[] toUTF8Bytes(CharSequence charSequence) { return toUTF8Bytes(charSequence, new BytesRefBuilder()); } public static byte[] toUTF8Bytes(CharSequence charSequence, BytesRefBuilder spare) { spare.copyChars(charSequence); return Arrays.copyOf(spare.bytes(), spare.length()); } /** * Return substring(beginIndex, endIndex) that is impervious to string length. */ public static String substring(String s, int beginIndex, int endIndex) { if (s == null) { return s; } int realEndIndex = s.length() > 0 ? s.length() - 1 : 0; if (endIndex > realEndIndex) { return s.substring(beginIndex); } else { return s.substring(beginIndex, endIndex); } } /** * If an array only consists of zero or one element, which is "*" or "_all" return an empty array * which is usually used as everything */ public static boolean isAllOrWildcard(String[] data) { return CollectionUtils.isEmpty(data) || data.length == 1 && ("_all".equals(data[0]) || "*".equals(data[0])); } /** * Return a {@link String} that is the json representation of the provided {@link ToXContent}. * Wraps the output into an anonymous object if needed. The content is not pretty-printed * nor human readable. */ public static String toString(ToXContent toXContent) { return toString(toXContent, false, false); } /** * Returns a string representation of the builder (only applicable for text based xcontent). * @param xContentBuilder builder containing an object to converted to a string */ public static String toString(XContentBuilder xContentBuilder) { return BytesReference.bytes(xContentBuilder).utf8ToString(); } /** * Return a {@link String} that is the json representation of the provided {@link ToXContent}. * Wraps the output into an anonymous object if needed. Allows to control whether the outputted * json needs to be pretty printed and human readable. * */ public static String toString(ToXContent toXContent, boolean pretty, boolean human) { try { XContentBuilder builder = createBuilder(pretty, human); if (toXContent.isFragment()) { builder.startObject(); } toXContent.toXContent(builder, ToXContent.EMPTY_PARAMS); if (toXContent.isFragment()) { builder.endObject(); } return toString(builder); } catch (IOException e) { try { XContentBuilder builder = createBuilder(pretty, human); builder.startObject(); builder.field("error", "error building toString out of XContent: " + e.getMessage()); builder.field("stack_trace", ExceptionsHelper.stackTrace(e)); builder.endObject(); return toString(builder); } catch (IOException e2) { throw new ElasticsearchException("cannot generate error message for deserialization", e); } } } private static XContentBuilder createBuilder(boolean pretty, boolean human) throws IOException { XContentBuilder builder = JsonXContent.contentBuilder(); if (pretty) { builder.prettyPrint(); } if (human) { builder.humanReadable(true); } return builder; } /** * Truncates string to a length less than length. Backtracks to throw out * high surrogates. */ public static String cleanTruncate(String s, int length) { if (s == null) { return s; } /* * Its pretty silly for you to truncate to 0 length but just in case * someone does this shouldn't break. */ if (length == 0) { return ""; } if (length >= s.length()) { return s; } if (Character.isHighSurrogate(s.charAt(length - 1))) { length--; } return s.substring(0, length); } public static boolean isNullOrEmpty(@Nullable String s) { return s == null || s.isEmpty(); } 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"); } 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(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy