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

org.apache.dubbo.common.utils.StringUtils Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.dubbo.common.utils;

import org.apache.dubbo.common.io.UnsafeStringWriter;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.lang.String.valueOf;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.DOT_REGEX;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.SEPARATOR_REGEX;
import static org.apache.dubbo.common.constants.CommonConstants.UNDERLINE_SEPARATOR;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_JSON_CONVERT_EXCEPTION;

/**
 * StringUtils
 */
public final class StringUtils {

    public static final String EMPTY_STRING = "";
    public static final int INDEX_NOT_FOUND = -1;
    public static final String[] EMPTY_STRING_ARRAY = new String[0];

    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(StringUtils.class);
    private static final Pattern KVP_PATTERN =
            Pattern.compile("([_.a-zA-Z0-9][-_.a-zA-Z0-9]*)[=](.*)"); // key value pair pattern.
    private static final Pattern NUM_PATTERN = Pattern.compile("^\\d+$");
    private static final Pattern PARAMETERS_PATTERN =
            Pattern.compile("^\\[((\\s*\\{\\s*[\\w_\\-\\.]+\\s*:\\s*.+?\\s*\\}\\s*,?\\s*)+)\\s*\\]$");
    private static final Pattern PAIR_PARAMETERS_PATTERN =
            Pattern.compile("^\\{\\s*([\\w-_\\.]+)\\s*:\\s*(.+)\\s*\\}$");
    private static final int PAD_LIMIT = 8192;
    private static final byte[] HEX2B;

    /**
     * @since 2.7.5
     */
    public static final char EQUAL_CHAR = '=';

    public static final String EQUAL = valueOf(EQUAL_CHAR);

    public static final char AND_CHAR = '&';

    public static final String AND = valueOf(AND_CHAR);

    public static final char SEMICOLON_CHAR = ';';

    public static final String SEMICOLON = valueOf(SEMICOLON_CHAR);

    public static final char QUESTION_MASK_CHAR = '?';

    public static final String QUESTION_MASK = valueOf(QUESTION_MASK_CHAR);

    public static final char SLASH_CHAR = '/';

    public static final String SLASH = valueOf(SLASH_CHAR);

    public static final char HYPHEN_CHAR = '-';

    public static final String HYPHEN = valueOf(HYPHEN_CHAR);

    static {
        HEX2B = new byte[128];
        Arrays.fill(HEX2B, (byte) -1);
        HEX2B['0'] = (byte) 0;
        HEX2B['1'] = (byte) 1;
        HEX2B['2'] = (byte) 2;
        HEX2B['3'] = (byte) 3;
        HEX2B['4'] = (byte) 4;
        HEX2B['5'] = (byte) 5;
        HEX2B['6'] = (byte) 6;
        HEX2B['7'] = (byte) 7;
        HEX2B['8'] = (byte) 8;
        HEX2B['9'] = (byte) 9;
        HEX2B['A'] = (byte) 10;
        HEX2B['B'] = (byte) 11;
        HEX2B['C'] = (byte) 12;
        HEX2B['D'] = (byte) 13;
        HEX2B['E'] = (byte) 14;
        HEX2B['F'] = (byte) 15;
        HEX2B['a'] = (byte) 10;
        HEX2B['b'] = (byte) 11;
        HEX2B['c'] = (byte) 12;
        HEX2B['d'] = (byte) 13;
        HEX2B['e'] = (byte) 14;
        HEX2B['f'] = (byte) 15;
    }

    private StringUtils() {}

    /**
     * Gets a CharSequence length or {@code 0} if the CharSequence is
     * {@code null}.
     *
     * @param cs a CharSequence or {@code null}
     * @return CharSequence length or {@code 0} if the CharSequence is
     * {@code null}.
     */
    public static int length(final CharSequence cs) {
        return cs == null ? 0 : cs.length();
    }

    /**
     * 

Repeat a String {@code repeat} times to form a * new String.

* *
     * StringUtils.repeat(null, 2) = null
     * StringUtils.repeat("", 0)   = ""
     * StringUtils.repeat("", 2)   = ""
     * StringUtils.repeat("a", 3)  = "aaa"
     * StringUtils.repeat("ab", 2) = "abab"
     * StringUtils.repeat("a", -2) = ""
     * 
* * @param str the String to repeat, may be null * @param repeat number of times to repeat str, negative treated as zero * @return a new String consisting of the original String repeated, * {@code null} if null String input */ public static String repeat(final String str, final int repeat) { // Performance tuned for 2.0 (JDK1.4) if (str == null) { return null; } if (repeat <= 0) { return EMPTY_STRING; } final int inputLength = str.length(); if (repeat == 1 || inputLength == 0) { return str; } if (inputLength == 1 && repeat <= PAD_LIMIT) { return repeat(str.charAt(0), repeat); } final int outputLength = inputLength * repeat; switch (inputLength) { case 1: return repeat(str.charAt(0), repeat); case 2: final char ch0 = str.charAt(0); final char ch1 = str.charAt(1); final char[] output2 = new char[outputLength]; for (int i = repeat * 2 - 2; i >= 0; i--, i--) { output2[i] = ch0; output2[i + 1] = ch1; } return new String(output2); default: final StringBuilder buf = new StringBuilder(outputLength); for (int i = 0; i < repeat; i++) { buf.append(str); } return buf.toString(); } } /** *

Repeat a String {@code repeat} times to form a * new String, with a String separator injected each time.

* *
     * StringUtils.repeat(null, null, 2) = null
     * StringUtils.repeat(null, "x", 2)  = null
     * StringUtils.repeat("", null, 0)   = ""
     * StringUtils.repeat("", "", 2)     = ""
     * StringUtils.repeat("", "x", 3)    = "xxx"
     * StringUtils.repeat("?", ", ", 3)  = "?, ?, ?"
     * 
* * @param str the String to repeat, may be null * @param separator the String to inject, may be null * @param repeat number of times to repeat str, negative treated as zero * @return a new String consisting of the original String repeated, * {@code null} if null String input * @since 2.5 */ public static String repeat(final String str, final String separator, final int repeat) { if (str == null || separator == null) { return repeat(str, repeat); } // given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it final String result = repeat(str + separator, repeat); return removeEnd(result, separator); } /** *

Removes a substring only if it is at the end of a source string, * otherwise returns the source string.

* *

A {@code null} source string will return {@code null}. * An empty ("") source string will return the empty string. * A {@code null} search string will return the source string.

* *
     * StringUtils.removeEnd(null, *)      = null
     * StringUtils.removeEnd("", *)        = ""
     * StringUtils.removeEnd(*, null)      = *
     * StringUtils.removeEnd("www.domain.com", ".com.")  = "www.domain.com"
     * StringUtils.removeEnd("www.domain.com", ".com")   = "www.domain"
     * StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com"
     * StringUtils.removeEnd("abc", "")    = "abc"
     * 
* * @param str the source String to search, may be null * @param remove the String to search for and remove, may be null * @return the substring with the string removed if found, * {@code null} if null String input */ public static String removeEnd(final String str, final String remove) { if (isAnyEmpty(str, remove)) { return str; } if (str.endsWith(remove)) { return str.substring(0, str.length() - remove.length()); } return str; } /** *

Returns padding using the specified delimiter repeated * to a given length.

* *
     * StringUtils.repeat('e', 0)  = ""
     * StringUtils.repeat('e', 3)  = "eee"
     * StringUtils.repeat('e', -2) = ""
     * 
* *

Note: this method doesn't not support padding with * Unicode Supplementary Characters * as they require a pair of {@code char}s to be represented. * If you are needing to support full I18N of your applications * consider using {@link #repeat(String, int)} instead. *

* * @param ch character to repeat * @param repeat number of times to repeat char, negative treated as zero * @return String with repeated character * @see #repeat(String, int) */ public static String repeat(final char ch, final int repeat) { final char[] buf = new char[repeat]; for (int i = repeat - 1; i >= 0; i--) { buf[i] = ch; } return new String(buf); } /** *

Strips any of a set of characters from the end of a String.

* *

A {@code null} input String returns {@code null}. * An empty string ("") input returns the empty string.

* *

If the stripChars String is {@code null}, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}.

* *
     * StringUtils.stripEnd(null, *)          = null
     * StringUtils.stripEnd("", *)            = ""
     * StringUtils.stripEnd("abc", "")        = "abc"
     * StringUtils.stripEnd("abc", null)      = "abc"
     * StringUtils.stripEnd("  abc", null)    = "  abc"
     * StringUtils.stripEnd("abc  ", null)    = "abc"
     * StringUtils.stripEnd(" abc ", null)    = " abc"
     * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
     * StringUtils.stripEnd("120.00", ".0")   = "12"
     * 
* * @param str the String to remove characters from, may be null * @param stripChars the set of characters to remove, null treated as whitespace * @return the stripped String, {@code null} if null String input */ public static String stripEnd(final String str, final String stripChars) { int end; if (str == null || (end = str.length()) == 0) { return str; } if (stripChars == null) { while (end != 0 && Character.isWhitespace(str.charAt(end - 1))) { end--; } } else if (stripChars.isEmpty()) { return str; } else { while (end != 0 && stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND) { end--; } } return str.substring(0, end); } /** *

Replaces all occurrences of a String within another String.

* *

A {@code null} reference passed to this method is a no-op.

* *
     * StringUtils.replace(null, *, *)        = null
     * StringUtils.replace("", *, *)          = ""
     * StringUtils.replace("any", null, *)    = "any"
     * StringUtils.replace("any", *, null)    = "any"
     * StringUtils.replace("any", "", *)      = "any"
     * StringUtils.replace("aba", "a", null)  = "aba"
     * StringUtils.replace("aba", "a", "")    = "b"
     * StringUtils.replace("aba", "a", "z")   = "zbz"
     * 
* * @param text text to search and replace in, may be null * @param searchString the String to search for, may be null * @param replacement the String to replace it with, may be null * @return the text with any replacements processed, * {@code null} if null String input * @see #replace(String text, String searchString, String replacement, int max) */ public static String replace(final String text, final String searchString, final String replacement) { return replace(text, searchString, replacement, -1); } /** *

Replaces a String with another String inside a larger String, * for the first {@code max} values of the search String.

* *

A {@code null} reference passed to this method is a no-op.

* *
     * StringUtils.replace(null, *, *, *)         = null
     * StringUtils.replace("", *, *, *)           = ""
     * StringUtils.replace("any", null, *, *)     = "any"
     * StringUtils.replace("any", *, null, *)     = "any"
     * StringUtils.replace("any", "", *, *)       = "any"
     * StringUtils.replace("any", *, *, 0)        = "any"
     * StringUtils.replace("abaa", "a", null, -1) = "abaa"
     * StringUtils.replace("abaa", "a", "", -1)   = "b"
     * StringUtils.replace("abaa", "a", "z", 0)   = "abaa"
     * StringUtils.replace("abaa", "a", "z", 1)   = "zbaa"
     * StringUtils.replace("abaa", "a", "z", 2)   = "zbza"
     * StringUtils.replace("abaa", "a", "z", -1)  = "zbzz"
     * 
* * @param text text to search and replace in, may be null * @param searchString the String to search for, may be null * @param replacement the String to replace it with, may be null * @param max maximum number of values to replace, or {@code -1} if no maximum * @return the text with any replacements processed, * {@code null} if null String input */ public static String replace(final String text, final String searchString, final String replacement, int max) { if (isAnyEmpty(text, searchString) || replacement == null || max == 0) { return text; } int start = 0; int end = text.indexOf(searchString, start); if (end == INDEX_NOT_FOUND) { return text; } final int replLength = searchString.length(); int increase = replacement.length() - replLength; increase = increase < 0 ? 0 : increase; increase *= max < 0 ? 16 : max > 64 ? 64 : max; final StringBuilder buf = new StringBuilder(text.length() + increase); while (end != INDEX_NOT_FOUND) { buf.append(text, start, end).append(replacement); start = end + replLength; if (--max == 0) { break; } end = text.indexOf(searchString, start); } buf.append(text.substring(start)); return buf.toString(); } public static boolean isBlank(CharSequence cs) { int strLen; if (cs == null || (strLen = cs.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(cs.charAt(i))) { return false; } } return true; } /** * is not blank string. * * @param cs source string. * @return is not blank. */ public static boolean isNotBlank(CharSequence cs) { return !isBlank(cs); } /** * Check the cs String whether contains non whitespace characters. * * @param cs * @return */ public static boolean hasText(CharSequence cs) { return !isBlank(cs); } /** * is empty string. * * @param str source string. * @return is empty. */ public static boolean isEmpty(String str) { return str == null || str.isEmpty(); } /** *

Checks if the strings contain empty or null elements.

* *

     * StringUtils.isNoneEmpty(null)            = false
     * StringUtils.isNoneEmpty("")              = false
     * StringUtils.isNoneEmpty(" ")             = true
     * StringUtils.isNoneEmpty("abc")           = true
     * StringUtils.isNoneEmpty("abc", "def")    = true
     * StringUtils.isNoneEmpty("abc", null)     = false
     * StringUtils.isNoneEmpty("abc", "")       = false
     * StringUtils.isNoneEmpty("abc", " ")      = true
     * 
* * @param ss the strings to check * @return {@code true} if all strings are not empty or null */ public static boolean isNoneEmpty(final String... ss) { if (ArrayUtils.isEmpty(ss)) { return false; } for (final String s : ss) { if (isEmpty(s)) { return false; } } return true; } /** *

Checks if the strings contain at least on empty or null element.

* *

     * StringUtils.isAnyEmpty(null)            = true
     * StringUtils.isAnyEmpty("")              = true
     * StringUtils.isAnyEmpty(" ")             = false
     * StringUtils.isAnyEmpty("abc")           = false
     * StringUtils.isAnyEmpty("abc", "def")    = false
     * StringUtils.isAnyEmpty("abc", null)     = true
     * StringUtils.isAnyEmpty("abc", "")       = true
     * StringUtils.isAnyEmpty("abc", " ")      = false
     * 
* * @param ss the strings to check * @return {@code true} if at least one in the strings is empty or null */ public static boolean isAnyEmpty(final String... ss) { return !isNoneEmpty(ss); } /** * is not empty string. * * @param str source string. * @return is not empty. */ public static boolean isNotEmpty(String str) { return !isEmpty(str); } /** * if s1 is null and s2 is null, then return true * * @param s1 str1 * @param s2 str2 * @return equals */ public static boolean isEquals(String s1, String s2) { if (s1 == null && s2 == null) { return true; } if (s1 == null || s2 == null) { return false; } return s1.equals(s2); } /** * is positive integer or zero string. * * @param str a string * @return is positive integer or zero */ public static boolean isNumber(String str) { return isNotEmpty(str) && NUM_PATTERN.matcher(str).matches(); } /** * parse str to Integer(if str is not number or n < 0, then return 0) * * @param str a number str * @return positive integer or zero */ public static int parseInteger(String str) { return isNumber(str) ? Integer.parseInt(str) : 0; } /** * parse str to Long(if str is not number or n < 0, then return 0) * * @param str a number str * @return positive long or zero */ public static long parseLong(String str) { return isNumber(str) ? Long.parseLong(str) : 0; } /** * Returns true if s is a legal Java identifier.

* more info. */ public static boolean isJavaIdentifier(String s) { if (isEmpty(s) || !Character.isJavaIdentifierStart(s.charAt(0))) { return false; } for (int i = 1; i < s.length(); i++) { if (!Character.isJavaIdentifierPart(s.charAt(i))) { return false; } } return true; } public static boolean isContains(String values, String value) { return isNotEmpty(values) && isContains(COMMA_SPLIT_PATTERN.split(values), value); } public static boolean isContains(String str, char ch) { return isNotEmpty(str) && str.indexOf(ch) >= 0; } public static boolean isNotContains(String str, char ch) { return !isContains(str, ch); } /** * @param values * @param value * @return contains */ public static boolean isContains(String[] values, String value) { if (isNotEmpty(value) && ArrayUtils.isNotEmpty(values)) { for (String v : values) { if (value.equals(v)) { return true; } } } return false; } public static boolean isNumeric(String str, boolean allowDot) { if (str == null || str.isEmpty()) { return false; } boolean hasDot = false; int sz = str.length(); for (int i = 0; i < sz; i++) { if (str.charAt(i) == '.') { if (hasDot || !allowDot) { return false; } hasDot = true; continue; } if (!Character.isDigit(str.charAt(i))) { return false; } } return true; } /** * @param e * @return string */ public static String toString(Throwable e) { UnsafeStringWriter w = new UnsafeStringWriter(); PrintWriter p = new PrintWriter(w); p.print(e.getClass().getName()); if (e.getMessage() != null) { p.print(": " + e.getMessage()); } p.println(); try { e.printStackTrace(p); return w.toString(); } finally { p.close(); } } /** * @param msg * @param e * @return string */ public static String toString(String msg, Throwable e) { UnsafeStringWriter w = new UnsafeStringWriter(); w.write(msg + "\n"); PrintWriter p = new PrintWriter(w); try { e.printStackTrace(p); return w.toString(); } finally { p.close(); } } /** * translate. * * @param src source string. * @param from src char table. * @param to target char table. * @return String. */ public static String translate(String src, String from, String to) { if (isEmpty(src)) { return src; } StringBuilder sb = null; int ix; char c; for (int i = 0, len = src.length(); i < len; i++) { c = src.charAt(i); ix = from.indexOf(c); if (ix == -1) { if (sb != null) { sb.append(c); } } else { if (sb == null) { sb = new StringBuilder(len); sb.append(src, 0, i); } if (ix < to.length()) { sb.append(to.charAt(ix)); } } } return sb == null ? src : sb.toString(); } /** * split. * * @param ch char. * @return string array. */ public static String[] split(String str, char ch) { if (isEmpty(str)) { return EMPTY_STRING_ARRAY; } return splitToList0(str, ch).toArray(EMPTY_STRING_ARRAY); } private static List splitToList0(String str, char ch) { List result = new ArrayList<>(); int ix = 0, len = str.length(); for (int i = 0; i < len; i++) { if (str.charAt(i) == ch) { result.add(str.substring(ix, i)); ix = i + 1; } } if (ix >= 0) { result.add(str.substring(ix)); } return result; } /** * Splits String around matches of the given character. *

* Note: Compare with {@link StringUtils#split(String, char)}, this method reduce memory copy. */ public static List splitToList(String str, char ch) { if (isEmpty(str)) { return Collections.emptyList(); } return splitToList0(str, ch); } /** * Split the specified value to be a {@link Set} * * @param value the content to be split * @param separatorChar a char to separate * @return non-null read-only {@link Set} * @since 2.7.8 */ public static Set splitToSet(String value, char separatorChar) { return splitToSet(value, separatorChar, false); } /** * Split the specified value to be a {@link Set} * * @param value the content to be split * @param separatorChar a char to separate * @param trimElements require to trim the elements or not * @return non-null read-only {@link Set} * @since 2.7.8 */ public static Set splitToSet(String value, char separatorChar, boolean trimElements) { List values = splitToList(value, separatorChar); int size = values.size(); if (size < 1) { // empty condition return emptySet(); } if (!trimElements) { // Do not require to trim the elements return new LinkedHashSet(values); } return unmodifiableSet(values.stream().map(String::trim).collect(LinkedHashSet::new, Set::add, Set::addAll)); } /** * join string. * * @param array String array. * @return String. */ public static String join(String[] array) { if (ArrayUtils.isEmpty(array)) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); for (String s : array) { sb.append(s); } return sb.toString(); } /** * join string like javascript. * * @param array String array. * @param split split * @return String. */ public static String join(String[] array, char split) { if (ArrayUtils.isEmpty(array)) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; i++) { if (i > 0) { sb.append(split); } sb.append(array[i]); } return sb.toString(); } /** * join string like javascript. * * @param array String array. * @param split split * @return String. */ public static String join(String[] array, String split) { if (ArrayUtils.isEmpty(array)) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < array.length; i++) { if (i > 0) { sb.append(split); } sb.append(array[i]); } return sb.toString(); } public static String join(Collection coll, String split) { if (CollectionUtils.isEmpty(coll)) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (String s : coll) { if (isFirst) { isFirst = false; } else { sb.append(split); } sb.append(s); } return sb.toString(); } public static String join(final Object[] array, final char delimiter, final int startIndex, final int endIndex) { if (ArrayUtils.isEmpty(array)) { return EMPTY_STRING; } if (endIndex - startIndex <= 0) { return EMPTY_STRING; } StringBuilder sb = new StringBuilder(); for (int i = startIndex; i < endIndex; i++) { if (i > 0) { sb.append(delimiter); } sb.append(array[i]); } return sb.toString(); } /** * parse key-value pair. * * @param str string. * @param itemSeparator item separator. * @return key-value map; */ private static Map parseKeyValuePair(String str, String itemSeparator) { String[] tmp = str.split(itemSeparator); Map map = new HashMap<>(tmp.length); for (int i = 0; i < tmp.length; i++) { Matcher matcher = KVP_PATTERN.matcher(tmp[i]); if (!matcher.matches()) { continue; } map.put(matcher.group(1), matcher.group(2)); } return map; } public static String getQueryStringValue(String qs, String key) { Map map = parseQueryString(qs); return map.get(key); } /** * parse query string to Parameters. * * @param qs query string. * @return Parameters instance. */ public static Map parseQueryString(String qs) { if (isEmpty(qs)) { return new HashMap<>(); } return parseKeyValuePair(qs, "\\&"); } public static String getServiceKey(Map ps) { StringBuilder buf = new StringBuilder(); String group = ps.get(GROUP_KEY); if (isNotEmpty(group)) { buf.append(group).append('/'); } buf.append(ps.get(INTERFACE_KEY)); String version = ps.get(VERSION_KEY); if (isNotEmpty(group)) { buf.append(':').append(version); } return buf.toString(); } public static String toQueryString(Map ps) { StringBuilder buf = new StringBuilder(); if (ps != null && ps.size() > 0) { for (Map.Entry entry : new TreeMap(ps).entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (isNoneEmpty(key, value)) { if (buf.length() > 0) { buf.append('&'); } buf.append(key); buf.append('='); buf.append(value); } } } return buf.toString(); } public static String camelToSplitName(String camelName, String split) { if (isEmpty(camelName)) { return camelName; } if (!isWord(camelName)) { // convert Ab-Cd-Ef to ab-cd-ef if (isSplitCase(camelName, split.charAt(0))) { return camelName.toLowerCase(); } // not camel case return camelName; } StringBuilder buf = null; for (int i = 0; i < camelName.length(); i++) { char ch = camelName.charAt(i); if (ch >= 'A' && ch <= 'Z') { if (buf == null) { buf = new StringBuilder(); if (i > 0) { buf.append(camelName, 0, i); } } if (i > 0) { buf.append(split); } buf.append(Character.toLowerCase(ch)); } else if (buf != null) { buf.append(ch); } } return buf == null ? camelName.toLowerCase() : buf.toString().toLowerCase(); } private static boolean isSplitCase(String str, char separator) { if (str == null) { return false; } return str.chars().allMatch(ch -> (ch == separator) || isWord((char) ch)); } private static boolean isWord(String str) { if (str == null) { return false; } return str.chars().allMatch(ch -> isWord((char) ch)); } private static boolean isWord(char ch) { if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) { return true; } return false; } /** * Convert snake_case or SNAKE_CASE to kebab-case. *

* NOTE: Return itself if it's not a snake case. * * @param snakeName * @param split * @return */ public static String snakeToSplitName(String snakeName, String split) { String lowerCase = snakeName.toLowerCase(); if (isSnakeCase(snakeName)) { return replace(lowerCase, "_", split); } return snakeName; } protected static boolean isSnakeCase(String str) { return str.contains("_") || str.equals(str.toLowerCase()) || str.equals(str.toUpperCase()); } /** * Convert camelCase or snake_case/SNAKE_CASE to kebab-case * * @param str * @param split * @return */ public static String convertToSplitName(String str, String split) { if (isSnakeCase(str)) { return snakeToSplitName(str, split); } else { return camelToSplitName(str, split); } } public static String toArgumentString(Object[] args) { StringBuilder buf = new StringBuilder(); for (Object arg : args) { if (buf.length() > 0) { buf.append(COMMA_SEPARATOR); } if (arg == null || ReflectUtils.isPrimitives(arg.getClass())) { buf.append(arg); } else { try { buf.append(JsonUtils.toJson(arg)); } catch (Exception e) { logger.warn(COMMON_JSON_CONVERT_EXCEPTION, "", "", e.getMessage(), e); buf.append(arg); } } } return buf.toString(); } public static String trim(String str) { return str == null ? null : str.trim(); } public static String toURLKey(String key) { return key.toLowerCase().replaceAll(SEPARATOR_REGEX, HIDE_KEY_PREFIX); } public static String toOSStyleKey(String key) { key = key.toUpperCase().replaceAll(DOT_REGEX, UNDERLINE_SEPARATOR); if (!key.startsWith("DUBBO_")) { key = "DUBBO_" + key; } return key; } public static boolean isAllUpperCase(String str) { if (str != null && !isEmpty(str)) { int sz = str.length(); for (int i = 0; i < sz; ++i) { if (!Character.isUpperCase(str.charAt(i))) { return false; } } return true; } else { return false; } } public static String[] delimitedListToStringArray(String str, String delimiter) { return delimitedListToStringArray(str, delimiter, (String) null); } public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { if (str == null) { return new String[0]; } else if (delimiter == null) { return new String[] {str}; } else { List result = new ArrayList(); int pos; if ("".equals(delimiter)) { for (pos = 0; pos < str.length(); ++pos) { result.add(deleteAny(str.substring(pos, pos + 1), charsToDelete)); } } else { int delPos; for (pos = 0; (delPos = str.indexOf(delimiter, pos)) != -1; pos = delPos + delimiter.length()) { result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); } if (str.length() > 0 && pos <= str.length()) { result.add(deleteAny(str.substring(pos), charsToDelete)); } } return toStringArray((Collection) result); } } public static String arrayToDelimitedString(Object[] arr, String delim) { if (ArrayUtils.isEmpty(arr)) { return ""; } else if (arr.length == 1) { return nullSafeToString(arr[0]); } else { StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; ++i) { if (i > 0) { sb.append(delim); } sb.append(arr[i]); } return sb.toString(); } } public static String deleteAny(String inString, String charsToDelete) { if (isNotEmpty(inString) && isNotEmpty(charsToDelete)) { 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(); } else { return inString; } } public static String[] toStringArray(Collection collection) { return (String[]) collection.toArray(new String[0]); } public static String nullSafeToString(Object obj) { if (obj == null) { return "null"; } else if (obj instanceof String) { return (String) obj; } else { String str = obj.toString(); return str != null ? str : ""; } } /** * Decode parameters string to map * * @param rawParameters format like '[{a:b},{c:d}]' * @return */ public static Map parseParameters(String rawParameters) { if (StringUtils.isBlank(rawParameters)) { return Collections.emptyMap(); } Matcher matcher = PARAMETERS_PATTERN.matcher(rawParameters); if (!matcher.matches()) { return Collections.emptyMap(); } String pairs = matcher.group(1); String[] pairArr = pairs.split("\\s*,\\s*"); Map parameters = new HashMap<>(); for (String pair : pairArr) { Matcher pairMatcher = PAIR_PARAMETERS_PATTERN.matcher(pair); if (pairMatcher.matches()) { parameters.put(pairMatcher.group(1), pairMatcher.group(2)); } } return parameters; } /** * Encode parameters map to string, like '[{a:b},{c:d}]' * * @param params * @return */ public static String encodeParameters(Map params) { if (params == null || params.isEmpty()) { return null; } StringBuilder sb = new StringBuilder(); sb.append('['); params.forEach((key, value) -> { // {key:value}, if (hasText(value)) { sb.append('{').append(key).append(':').append(value).append("},"); } }); // delete last separator ',' if (sb.charAt(sb.length() - 1) == ',') { sb.deleteCharAt(sb.length() - 1); } sb.append(']'); return sb.toString(); } public static int decodeHexNibble(final char c) { // Character.digit() is not used here, as it addresses a larger // set of characters (both ASCII and full-width latin letters). byte[] hex2b = HEX2B; return c < hex2b.length ? hex2b[c] : -1; } /** * Decode a 2-digit hex byte from within a string. */ public static byte decodeHexByte(CharSequence s, int pos) { int hi = decodeHexNibble(s.charAt(pos)); int lo = decodeHexNibble(s.charAt(pos + 1)); if (hi == -1 || lo == -1) { throw new IllegalArgumentException( String.format("invalid hex byte '%s' at index %d of '%s'", s.subSequence(pos, pos + 2), pos, s)); } return (byte) ((hi << 4) + lo); } /** * Create the common-delimited {@link String} by one or more {@link String} members * * @param one one {@link String} * @param others others {@link String} * @return null if one or others is null * @since 2.7.8 */ public static String toCommaDelimitedString(String one, String... others) { String another = arrayToDelimitedString(others, COMMA_SEPARATOR); return isEmpty(another) ? one : one + COMMA_SEPARATOR + another; } /** * Test str whether starts with the prefix ignore case. */ public static boolean startsWithIgnoreCase(String str, String prefix) { if (str == null || prefix == null || str.length() < prefix.length()) { return false; } // return str.substring(0, prefix.length()).equalsIgnoreCase(prefix); return str.regionMatches(true, 0, prefix, 0, prefix.length()); } public static String defaultIf(String str, String defaultStr) { return isEmpty(str) ? defaultStr : str; } /** * Returns a substring from 'str' between 'start' and 'end', or to the end if 'end' is -1 */ public static String substring(String str, int start, int end) { if (str == null) { return null; } return end == INDEX_NOT_FOUND ? str.substring(start) : str.substring(start, end); } /** * Extracts a substring from the given string that precedes the first occurrence of the specified character separator. * If the character is not found, the entire string is returned. */ public static String substringBefore(String str, int separator) { if (isEmpty(str)) { return str; } int index = str.indexOf(separator); return index == INDEX_NOT_FOUND ? str : str.substring(0, index); } /** * Extracts a substring from the given string that precedes the first occurrence of the specified string separator. * If the separator is not found or is null, the entire string is returned. */ public static String substringBefore(String str, String separator) { if (isEmpty(str) || separator == null) { return str; } if (separator.isEmpty()) { return EMPTY_STRING; } int index = str.indexOf(separator); return index == INDEX_NOT_FOUND ? str : str.substring(0, index); } /** * Tokenize the given String into a String array. * Trims tokens and omits empty tokens. */ public static String[] tokenize(String str, char... separators) { if (isEmpty(str)) { return EMPTY_STRING_ARRAY; } return tokenizeToList(str, separators).toArray(EMPTY_STRING_ARRAY); } public static List tokenizeToList(String str, char... separators) { if (isEmpty(str)) { return Collections.emptyList(); } if (separators == null || separators.length == 0) { separators = new char[] {','}; } List tokens = new ArrayList<>(); int start = -1, end = 0; int i = 0; out: for (int len = str.length(), sLen = separators.length; i < len; i++) { char c = str.charAt(i); for (int j = 0; j < sLen; j++) { if (c == separators[j]) { if (start > -1) { tokens.add(str.substring(start, end + 1)); start = -1; } continue out; } } switch (c) { case ' ': case '\t': case '\n': case '\r': break; default: if (start == -1) { start = i; } end = i; break; } } if (start > -1) { String part = str.substring(start, end + 1); if (tokens.isEmpty()) { return Collections.singletonList(part); } tokens.add(part); } return tokens; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy