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

org.seppiko.commons.utils.StringUtil Maven / Gradle / Ivy

There is a newer version: 2.11.0
Show newest version
/*
 * Copyright 2023 the original author or authors.
 *
 * Licensed 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.seppiko.commons.utils;

import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Matcher;

/**
 * String Util
 *
 * @author Leonard Woo
 */
public class StringUtil {

  private StringUtil() {}

  /**
   * Require a CharSequence with default value.
   *
   * @param value CharSequence instance.
   * @param defaultValue default CharSequence instance.
   * @return CharSequence without null.
   */
  public static CharSequence requireNonBlankElse(CharSequence value, CharSequence defaultValue) {
    return isNullOrEmpty(value)? defaultValue: value;
  }

  /**
   * Test CharSequence is empty or contains only whitespace codepoints
   *
   * @param value CharSequence instance.
   * @return true is yes.
   */
  public static boolean isNullOrEmpty(CharSequence value) {
    return Objects.isNull(value) || value.isEmpty();
  }

  /**
   * Test CharSequence has any char
   *
   * @param input CharSequence instance.
   * @return true is yes.
   */
  public static boolean hasLength(CharSequence input) {
    return Objects.nonNull(input) && (input.length() > 0);
  }

  /**
   * Test CharSequence has any char without non-blank character
   *
   * @param input CharSequence instance.
   * @return true is not null and without whitespace, false is otherwise.
   */
  public static boolean hasText(CharSequence input) {
    return hasLength(input) && containsText(input);
  }

  /**
   * Test CharSequence is not null and empty or has non-blank character
   *
   * @param input CharSequence instance.
   * @return true is not null and empty, false is otherwise.
   */
  public static boolean nonText(CharSequence input) {
    return Objects.nonNull(input) && (input.isEmpty() || !containsText(input));
  }

  // check input include whitespace or ISO control chars.
  private static boolean containsText(CharSequence input) {
    for (int i = 0; i < input.length(); i++) {
      char ch = input.charAt(i);
      if (!Character.isWhitespace(ch) || !Character.isISOControl(ch)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Test CharSequence is uppercase
   *
   * @param input uppercase CharSequence.
   * @return true is uppercase.
   */
  public static boolean isUppercase(CharSequence input) {
    return RegexUtil.matches("^[\\p{Upper}A-Z]+$", input);
  }

  /**
   * Test CharSequence is lowercase
   *
   * @param input lowercase CharSequence.
   * @return true is lowercase.
   */
  public static boolean isLowercase(CharSequence input) {
    return RegexUtil.matches("^[\\p{Lower}a-z]+$", input);
  }

  /**
   * Test CharSequence is numeric
   *
   * @param input numeric CharSequence.
   * @return true is numeric.
   */
  public static boolean isNumeric(CharSequence input) {
    return RegexUtil.matches("^[--++]?[\\d0-9]+([\\..]{1}[\\d0-9]+)?$", input);
  }

  /**
   * Test CharSequence is integer
   *
   * @param input integer CharSequence.
   * @return true is integer.
   */
  public static boolean isInteger(CharSequence input) {
    return RegexUtil.matches("^[--++]?[\\d0-9]+$", input);
  }

  /**
   * Test CharSequence is decimal
   *
   * @param input decimal CharSequence.
   * @return true is decimal.
   */
  public static boolean isDecimal(CharSequence input) {
    return RegexUtil.matches("^[--++]?[\\d0-9]+[\\..]{1}[\\d0-9]+$", input);
  }

  /**
   * Test CharSequence is punctuation
   *
   * @param input punctuation CharSequence.
   * @return true is punctuation.
   */
  public static boolean isPunctuation(CharSequence input) {
    return RegexUtil.matches("^[\\p{Punct}!"#$%&'()*+,-./:;<=>?@[\]^_`{|}]+$", input);
  }

  /**
   * Convert string data from old encoding to new encoding
   *
   * @param data CharSequence data.
   * @param oldEncoding old encoding.
   * @param newEncoding new encoding.
   * @return new encoding CharSequence.
   * @throws NullPointerException old encode or new decode exception.
   */
  public static String transcoding(CharSequence data, Charset oldEncoding, Charset newEncoding)
      throws NullPointerException {
    byte[] oldData = CharUtil.charsetEncode(oldEncoding, CharBuffer.wrap(data));
    if (oldData == Environment.EMPTY_BYTE_ARRAY) {
      throw new NullPointerException("old encode exception");
    }
    CharBuffer cb = CharUtil.charsetDecode(newEncoding, oldData);
    if (cb.capacity() == 0) {
      throw new NullPointerException("new decode exception");
    }
    return cb.toString();
  }

  /**
   * Return fixed length string object
   *
   * @param str string object.
   * @param length count length.
   * @param preChar pre-padded character.
   * @return fixed length string object.
   */
  public static String fixedLength(String str, int length, char preChar) {
    StringBuilder sb = new StringBuilder();
    if (str.length() < length) {
      int preLength = length - str.length();
      sb.append(String.valueOf(preChar).repeat(preLength));
      sb.append(str);
    } else {
      sb.append(str.substring(str.length() - length));
    }
    return sb.toString();
  }

  /**
   * Capitalize the first letter
   *
   * @param input origin string.
   * @return new string.
   */
  public static String toFirstUpperCase(String input) {
    String s = String.valueOf(Character.toUpperCase(input.charAt(0)));
    if (input.length() > 1) {
      return s + input.subSequence(1, input.length());
    }
    return s;
  }

  /**
   * Replace CharSequence between start and end
   *
   * @param data origin data.
   * @param start replace start index.
   * @param end replace end index.
   * @param replacement replace data.
   * @return new string.
   */
  public static String replaceBetween(
      CharSequence data, int start, int end, CharSequence replacement) {
    StringBuilder sb = new StringBuilder();
    sb.append(data, 0, start);
    sb.append(replacement);
    sb.append(data, end, data.length());
    return sb.toString();
  }

  /**
   * convert joiner string with delimiter
   *
   * @param delimiter the sequence of characters to be used between each array element.
   * @param strs string array.
   * @return the string representation.
   */
  public static String convertJoinerString(String delimiter, String... strs) {
    StringJoiner sj = new StringJoiner(delimiter);
    for (String str : strs) {
      if (hasLength(str)) {
        sj.add(str);
      }
    }
    return sj.toString();
  }

  /**
   * Convert Char Sequence to String with separate
   *
   * @param input Raw data.
   * @param splitNum Separation interval.
   * @param split Separator.
   * @return encoded string.
   */
  public static String convertToString(CharSequence input, int splitNum, String split) {
    StringBuilder sb = new StringBuilder();
    // add split in every splitNum
    for (int i = 0; i < input.length(); i = i + splitNum) {
      for (int j = 0; j < splitNum; j++) {
        sb.append(input.charAt(i + j));
      }
      sb.append(split);
    }
    String dst = sb.toString();
    return dst.substring(0, dst.length() - split.length()); // delete last split
  }

  /**
   * Delete string separator and to char array.
   *
   * @param src string.
   * @param separator separator.
   * @return char array.
   * @throws NullPointerException when data or separator is null.
   */
  public static char[] convertToCharArray(String src, String separator) throws NullPointerException {
    Objects.requireNonNull(src, "src must not be null.");
    Objects.requireNonNull(separator, "separator must not be null.");
    Matcher m = RegexUtil.getMatcher(separator, src);
    Objects.requireNonNull(m, "split matcher failed");
    if (m.find()) {
      return m.replaceAll("").toCharArray();
    }
    return src.toCharArray();
  }

  /**
   * Split key value pair string to the map.
   *
   * @param input The key value pair string to be split.
   * @param nodeSeparator Split the input string on this delimiter with regex.
   * @param pairSeparator For each part, split with this delimiter with regex.
   * @return key value pair map.
   */
  public static Map splitToMap(String input, String nodeSeparator, String pairSeparator) {
    Map map = new HashMap<>();
    if (!input.isEmpty()) {
      String[] parts = RegexUtil.split(nodeSeparator, input);
      for (String part : parts) {
        String[] pair = RegexUtil.split(pairSeparator, part);
        map.put(pair[0], pair[1]);
      }
    }
    return map;
  }

  /** between {@code '\uFF01'} and {\u0021} number */
  protected static final Integer BETWEEN =
      (CharUtil.FULLWIDTH_EXCLAMATION_MARK - CharUtil.EXCLAMATION_MARK);

  /**
   * Convert half-width string ({@code '\u005Cu0021'} through {@code '\u005Cu007E'}) to
   * full-width string ({@code '\u005CuFF01'} through {@code '\u005CuFF5E'})
   *
   * @param src Half-width string.
   * @return Full-width string.
   */
  public static String toFullWidth(CharSequence src) {
    StringBuilder sb = new StringBuilder(src.length());
    for (int i = 0; i < src.length(); i++) {
      char c = src.charAt(i);
      if (NumberUtil.between(c, CharUtil.EXCLAMATION_MARK, CharUtil.TILDE)) {
        sb.append((char) (c + BETWEEN));
      } else {
        sb.append(c);
      }
    }
    return sb.toString();
  }

  /**
   * Convert full-width string ({@code '\u005CuFF01'} through {@code '\u005CuFF5E'}) to
   * half-width string ({@code '\u005Cu0021'} through {@code '\u005Cu007E'})
   *
   * @param src Full-width string.
   * @return Half-width string.
   */
  public static String toHalfWidth(CharSequence src) {
    StringBuilder sb = new StringBuilder(src.length());
    for (int i = 0; i < src.length(); i++) {
      char c = src.charAt(i);
      if (NumberUtil.between(c, CharUtil.FULLWIDTH_EXCLAMATION_MARK, CharUtil.FULLWIDTH_TILDE)) {
        sb.append((char) (c - BETWEEN));
      } else {
        sb.append(c);
      }
    }
    return sb.toString();
  }

  /**
   * Unicode decoding.
   * When not found return origin String
   *
   * @param src unicode string like {@code '\\uXXXX'}.
   * @return String. If src is null or empty return empty string.
   */
  public static String unicodeDecode(String src) {
    if (isNullOrEmpty(src)) {
      return "";
    }
    Matcher m = RegexUtil.getMatcher("(\\\\u(\\p{XDigit}{4}))", src);
    while (m.find()) {
      String group = m.group(2);
      char ch = (char) Integer.parseInt(group, 16);
      String group1 = m.group(1);
      src = src.replace(group1, String.valueOf(ch));
    }
    return src;
  }

  /**
   * Unicode encoding
   *
   * @param src String
   * @return unicode string like {@code '\\uXXXX'}.  If src is null or empty return empty string.
   */
  public static String unicodeEncode(String src) {
    if (isNullOrEmpty(src)) {
      return "";
    }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < src.length(); i++) {
      char ch = src.charAt(i);
      if (NumberUtil.between(ch, CharUtil.SPACE, CharUtil.TILDE)) {
        sb.append(ch);
      } else {
        sb.append("\\u");
        String hex = Integer.toHexString(ch & 0xFFFF).toUpperCase();
        sb.append(fixedLength(hex, 4, CharUtil.DIGIT_ONE));
      }
    }
    return sb.toString();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy