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

org.beangle.commons.lang.Strings.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2005, The Beangle Software.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

package org.beangle.commons.lang

import org.beangle.commons.collection.Collections

import java.lang.Character.{toLowerCase, isLowerCase as isLower, isUpperCase as isUpper}
import scala.collection.mutable

/** Operations on String that are {@code null} safe.
  *
  * @author chaostone 2005-11-15
  * @since 3.0
  */
object Strings {

  /** Constant DELIMITER=","
    */
  val DELIMITER = ","

  private val Empty = ""

  private val Index_not_found = -1

  /** Capitalizes a String changing the first letter to title case as per
    * {@link Character# toTitleCase ( char )}. No other letters are changed.
    * For a word based algorithm, see returns {@code null}.
    *
    * {{{
    * capitalize(null)  = null
    * capitalize("")    = ""
    * capitalize("cat") = "Cat"
    * capitalize("cAt") = "CAt"
    * }}}
    *
    * @param str the String to capitalize, may be null
    * @return the capitalized String, { @code null} if null String input
    * @see #uncapitalize(String)
    * @since 2.0
    */
  def capitalize(str: String): String = {
    if ((str eq null) || str.length == 0) return str
    val head = str.charAt(0)
    val upper = Character.toUpperCase(head)
    if (upper == head)
      str
    else {
      val chars = str.toCharArray
      chars(0) = upper
      new String(chars)
    }
  }

  /** concat.
    *
    * @param seq a String object.
    * @return a String object.
    */
  def concat(seq: Any*): String = join(seq, null)

  /** Checks if CharSequence contains a search CharSequence, handling {@code null}. This method uses
    * {@link String# indexOf ( String )} if possible.
    * A {@code null} CharSequence will return {@code false}.
    *
    * {{{
    * contains(null, *)     = false
    * contains(*, null)     = false
    * contains("", "")      = true
    * contains("abc", "")   = true
    * contains("abc", "a")  = true
    * contains("abc", "z")  = false
    * }}}
    *
    * @param seq       the CharSequence to check, may be null
    * @param searchSeq the CharSequence to find, may be null
    * @return true if the CharSequence contains the search CharSequence,
    *         false if not or { @code null} string input
    */
  def contains(seq: CharSequence, searchSeq: CharSequence): Boolean = {
    if (seq == null || searchSeq == null) return false
    indexOf(seq, searchSeq, 0) >= 0
  }

  /** Checks if CharSequence contains a search character, handling {@code null}. This method uses
    * {@link String# indexOf ( int )} if possible.
    * A {@code null} or empty ("") CharSequence will return {@code false}.
    *
    * {{{
    * contains(null, *)    = false
    * contains("", *)      = false
    * contains("abc", 'a') = true
    * contains("abc", 'z') = false
    * }}}
    *
    * @param seq        the CharSequence to check, may be null
    * @param searchChar the character to find
    * @return true if the CharSequence contains the search character,
    *         false if not or { @code null} string input
    * @since 2.0
    */
  def contains(seq: CharSequence, searchChar: Int): Boolean = {
    if (isEmpty(seq)) return false
    indexOf(seq, searchChar, 0) >= 0
  }

  /** count char in host string
    *
    * @param host      String object.
    * @param charactor a char.
    * @return a int.
    */
  def count(host: String, charactor: Char): Int = {
    var count = 0
    for (i <- 0 until host.length if host.charAt(i) == charactor) count += 1
    count
  }

  /** count inner string in host string
    *
    * @param host      a String object.
    * @param searchStr a String object.
    * @return a int.
    */
  def count(host: String, searchStr: String): Int = {
    var count = 0
    var startIndex = host.indexOf(searchStr, 0)
    while (startIndex > -1 && startIndex < host.length) {
      count += 1
      startIndex = host.indexOf(searchStr, startIndex + searchStr.length)
    }
    count
  }

  /** Returns index of searchChar in cs with begin index {@code start}
    *
    * @param searchChar
    * @param start
    */
  private def indexOf(cs: CharSequence, searchChar: CharSequence, start: Int): Int =
    cs.toString.indexOf(searchChar.toString, start)

  /** Finds the first index in the {@code CharSequence} that matches the specified character.
    *
    * @param cs         the { @code CharSequence} to be processed, not null
    * @param searchChar the char to be searched for
    * @param start      the start index, negative starts at the string start
    * @return the index where the search char was found, -1 if not found
    */
  private def indexOf(cs: CharSequence, searchChar: Int, start: Int): Int = {
    if cs.isInstanceOf[String] then
      cs.asInstanceOf[String].indexOf(searchChar, start)
    else
      ((if (start < 0) 0 else start) until cs.length).find(cs.charAt(_) == searchChar).getOrElse(-1)
  }

  /** insert.
    *
    * @param str a String object.
    * @param c   a String object.
    * @param pos a int.
    * @return a String object.
    */
  def insert(str: String, c: String, pos: Int): String = {
    if (str.length < pos) return str
    str.substring(0, pos - 1) + c + str.substring(pos)
  }

  /** replace [bigen,end] [1...end] with givenStr
    * 可以使用StringBuilder的replace方法替换该方法
    *
    * @param str      a String object.
    * @param beginAt  a int.
    * @param endAt    a int.
    * @param contnets a String object.
    * @return a String object.
    */
  def insert(str: String, contnets: String, beginAt: Int, endAt: Int): String = {
    if (beginAt < 1 || endAt > str.length || endAt < beginAt) return str
    str.substring(0, beginAt - 1) + contnets + str.substring(endAt)
  }

  /** intersectSeq.
    *
    * @param first  a String object.
    * @param second a String object.
    * @return a String object.
    */
  def intersectSeq(first: String, second: String): String = intersectSeq(first, second, DELIMITER)

  /** 返回一个新的逗号相隔字符串,实现其中的单词a-b的功能
    *
    * @param first     a String object.
    * @param second    a String object.
    * @param delimiter a String object.
    * @return a String object.
    */
  def intersectSeq(first: String, second: String, delimiter: String): String = {
    if (isEmpty(first) || isEmpty(second)) return ""
    val rs = Collections.intersection(split(first, ',').toList, split(second, ',').toList)
    val buf = new StringBuilder()
    rs foreach (ele => buf.append(delimiter).append(ele))
    if (buf.length > 0) buf.append(delimiter)
    buf.toString
  }

  /** Checks if a CharSequence is whitespace, empty ("") or null.
    *
    * {{{
    * isBlank(null)      = true
    * isBlank("")        = true
    * isBlank(" ")       = true
    * isBlank("bob")     = false
    * isBlank("  bob  ") = false
    * }}}
    *
    * @param cs
    * the CharSequence to check, may be null
    * @return { @code true} if the CharSequence is null, empty or whitespace
    * @since 3.0
    */
  def isBlank(cs: CharSequence): Boolean = {
    if ((cs eq null) || cs.length == 0) return true
    val strLen = cs.length
    !(0 until strLen).exists(i => !Character.isWhitespace(cs.charAt(i)))
  }

  /** Returns true is cs is null or cs.length equals 0.
    */
  @inline
  def isEmpty(cs: CharSequence): Boolean = (cs eq null) || 0 == cs.length

  /** isEqualSeq.
    *
    * @param first  not null
    * @param second not null
    * @return a boolean.
    */
  def isEqualSeq(first: String, second: String): Boolean = isEqualSeq(first, second, DELIMITER)

  /** 判断两个","逗号相隔的字符串中的单词是否完全等同.
    *
    * @param first     a String object.
    * @param second    a String object.
    * @param delimiter a String object.
    * @return a boolean.
    */
  def isEqualSeq(first: String, second: String, delimiter: String): Boolean =
    if (isNotEmpty(first) && isNotEmpty(second))
      split(first, delimiter).toSet == split(second, delimiter).toSet
    else
      isEmpty(first) & isEmpty(second)

  /** Checks if a CharSequence is not empty (""), not null and not whitespace only.
    *
    * {{{
    * isNotBlank(null)      = false
    * isNotBlank("")        = false
    * isNotBlank(" ")       = false
    * isNotBlank("bob")     = true
    * isNotBlank("  bob  ") = true
    * }}}
    *
    * @param cs the CharSequence to check, may be null
    * @return { @code true} if the CharSequence is not empty and not null and not whitespace
    * @since 3.0
    */
  def isNotBlank(cs: CharSequence): Boolean = !isBlank(cs)

  /** Return true if cs not null and cs has length.
    */
  @inline
  def isNotEmpty(cs: CharSequence): Boolean = !(cs eq null) && cs.length > 0

  /** join.
    *
    * @param seq
    * @param delimiter a String object.
    * @return a String object.
    */
  def join(seq: Iterable[_], delimiter: String): String =
    if (null == seq)
      ""
    else {
      val aim = new StringBuilder()
      for (one <- seq) {
        if (null != delimiter && aim.length > 0) aim.append(delimiter)
        aim.append(one)
      }
      aim.toString
    }

  /** join.
    *
    * @param seq a String object.
    * @return a String object.
    */
  def join(seq: String*): String = join(seq, DELIMITER)

  /** 将数组中的字符串,用delimiter串接起来.
* 首尾不加delimiter * * @param seq an array of String objects. * @param delimiter a String object. * @return a String object. */ def join(seq: Array[String], delimiter: String): String = if (null == seq) "" else { val seqLen = seq.length if (seqLen == 1) seq(0) else { val aim = new StringBuilder() (0 until seq.length) foreach { i => if (null != delimiter && i > 0) aim.append(delimiter) aim.append(seq(i)) } aim.toString } } /** 保持逗号分隔的各个单词都是唯一的。并且按照原来的顺序存放。 * * @param keyString a String object. * @return a String object. */ def keepSeqUnique(keyString: String): String = { val keyList = split(keyString, ",").toList val keys = keyList.toSet val keyBuf = new StringBuilder() val iter = keyList.iterator while (iter.hasNext) { val key = iter.next() if (!keys(key)) keyBuf.append(key) if (iter.hasNext) keyBuf.append(',') } keyBuf.toString } /** Left pad a String with a specified character. * Pad to a size of {@code size}. * * {{{ * leftPad(null, *, *) = null * leftPad("", 3, 'z') = "zzz" * leftPad("bat", 3, 'z') = "bat" * leftPad("bat", 5, 'z') = "zzbat" * leftPad("bat", 1, 'z') = "bat" * leftPad("bat", -1, 'z') = "bat" * }}} * * @param str the String to pad out, may be null * @param size the size to pad to * @param padChar the character to pad with * @return left padded String or original String if no padding is necessary, { @code null} if null * String input * @since 3.0 */ def leftPad(str: String, size: Int, padChar: Char): String = { if (str == null) return null val pads = size - str.length if (pads <= 0) return str repeat(padChar, pads).concat(str) } /** Right pad a String with a specified character. * The String is padded to the size of {@code size}. * * {{{ * rightPad(null, *, *) = null * rightPad("", 3, 'z') = "zzz" * rightPad("bat", 3, 'z') = "bat" * rightPad("bat", 5, 'z') = "batzz" * rightPad("bat", 1, 'z') = "bat" * rightPad("bat", -1, 'z') = "bat" * }}} * * @param str the String to pad out, may be null * @param size the size to pad to * @param padChar the character to pad with * @return right padded String or original String if no padding is necessary, { @code null} if null * String input * @since 3.0 */ def rightPad(str: String, size: Int, padChar: Char): String = { if (str == null) return null val pads = size - str.length if (pads <= 0) return str str.concat(repeat(padChar, pads)) } /** mergeSeq. * * @param first a String object. * @param second a String object. * @return a String object. */ def mergeSeq(first: String, second: String): String = mergeSeq(first, second, DELIMITER) /** 将两个用delimiter串起来的字符串,合并成新的串,重复的"单词"只出现一次. * 如果第一个字符串以delimiter开头,第二个字符串以delimiter结尾,
* 合并后的字符串仍以delimiter开头和结尾.
*

*

* * {{{ * mergeSeq(",1,2,", "") = ",1,2,"; * mergeSeq(",1,2,", null) = ",1,2,"; * mergeSeq("1,2", "3") = "1,2,3"; * mergeSeq("1,2", "3,") = "1,2,3,"; * mergeSeq(",1,2", "3,") = ",1,2,3,"; * mergeSeq(",1,2,", ",3,") = ",1,2,3,"; * }}} * *
* * @param first a String object. * @param second a String object. * @param delimiter a String object. * @return a String object. */ def mergeSeq(first: String, second: String, delimiter: String): String = if (isNotEmpty(second) && isNotEmpty(first)) { val firstSeq = split(first, delimiter).toList val secondSeq = split(second, delimiter).toList val rs = Collections.union(firstSeq, secondSeq) val buf = new StringBuilder() for (ele <- rs) buf.append(delimiter).append(ele) if (buf.length > 0) buf.append(delimiter) buf.toString } else (if ((first == null)) "" else first) + (if ((second == null)) "" else second) /** removeWord. * * @param host a String object. * @param word a String object. * @return a String object. */ def removeWord(host: String, word: String): String = removeWord(host, word, DELIMITER) /** removeWord. * * @param host a String object. * @param word a String object. * @param delimiter a String object. * @return a String object. */ def removeWord(host: String, word: String, delimiter: String): String = if (host.indexOf(word) == -1) host else { val beginIndex = host.indexOf(word) val endIndex = beginIndex + word.length if (beginIndex == 0) return host.substring(endIndex + 1) if (endIndex == host.length) host.substring(0, beginIndex - delimiter.length) else { val before = host.substring(0, beginIndex) val after = host.substring(endIndex + 1) before + after } } /** Returns padding using the specified delimiter repeated to a given length. * * {{{ * repeat(0, 'e') = "" * repeat(3, 'e') = "eee" * repeat(-2, 'e') = "" * }}} * * @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) */ def repeat(ch: Char, repeat: Int): String = { val buf = new Array[Char](repeat) var i = repeat - 1 while (i >= 0) { buf(i) = ch i -= 1 } new String(buf) } /** Repeat a String {@code repeat} times to form a new String. * * {{{ * repeat(null, 2) = null * repeat("", 0) = "" * repeat("", 2) = "" * repeat("a", 3) = "aaa" * repeat("ab", 2) = "abab" * 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 * @since 3.0 */ def repeat(str: String, repeat: Int): String = { if (str == null) return null if (repeat <= 1) { repeat >= 0 return if ((repeat == 0)) "" else str } val len = str.length val longSize = len.toLong * repeat.toLong val size = longSize.toInt if (size != longSize) throw new ArrayIndexOutOfBoundsException("Required array size too large: " + String.valueOf(longSize)) val array = new Array[Char](size) str.getChars(0, len, array, 0) var n = len while (n < size - n) { System.arraycopy(array, 0, array, n, n) n <<= 1 } System.arraycopy(array, 0, array, n, size - n) new String(array) } /** Replaces all occurrences of a String within another String. * A {@code null} reference passed to this method is a no-op. * * {{{ * replace(null, *, *) = null * replace("", *, *) = "" * replace("any", null, *) = "any" * replace("any", *, null) = "any" * replace("any", "", *) = "any" * replace("aba", "a", null) = "aba" * replace("aba", "a", "") = "b" * 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 */ def replace(text: String, searchString: String, replacement: String): String = { if (isEmpty(text) || isEmpty(searchString) || replacement == null) return text var start = 0 var end = text.indexOf(searchString, start) if (end == -1) return text val replLength = searchString.length var increase = replacement.length - replLength increase = if (increase < 0) 0 else increase increase *= 16 val buf = new StringBuilder(text.length + increase) while (end != -1) { buf.append(text.substring(start, end)).append(replacement) start = end + replLength end = text.indexOf(searchString, start) } buf.append(text.substring(start)) buf.toString } /** split. * * @param target a String object. * @return an array of String objects. */ def split(target: String): Array[String] = { split(target, Array(',', ';', '\r', '\n', ' ')) } /** Splits the provided text into an array, separator specified. This is an alternative to using * StringTokenizer. * A {@code null} input String returns {@code null}. * * {{{ * split(null, *) = null * split("", *) = [] * split("a.b.c", '.') = ["a", "b", "c"] * split("a..b.c", '.') = ["a", "b", "c"] * split("a:b:c", '.') = ["a:b:c"] * split("a b c", ' ') = ["a", "b", "c"] * }}} */ def split(str: String, separatorChar: Char): Array[String] = { if (str == null) return null val len = str.length if (len == 0) return new Array[String](0) val list = new mutable.ListBuffer[String] var i = 0 var start = 0 val length = str.length val chars = new Array[Char](length) str.getChars(0, length, chars, 0) while (i < len) { if (chars(i) == separatorChar) { //ignore continue separator if (start < i) addNonEmpty(list, chars, start, i) start = i + 1 } i += 1 } if (start < i) addNonEmpty(list, chars, start, i) list.toArray } def addNonEmpty(buffer: mutable.Buffer[String], chars: Array[Char], start: Int, end: Int): Unit = { val rs = new String(chars, start, end - start).trim() if rs.nonEmpty then buffer.addOne(rs) } /** split with separators * * @param target a String object. * @param separatorChars an array of char. * @return an array of String objects. */ def split(target: String, separatorChars: Array[Char]): Array[String] = { if (null == target) return new Array[String](0) if (separatorChars.length == 1) split(target, separatorChars(0)) else { val first = separatorChars(0) val all = separatorChars.toSet val sb = target.toCharArray for (i <- 0 until sb.length if all.contains(sb(i))) sb(i) = first split(new String(sb), first) } } /** Splits the provided text into an array, separators specified. This is an alternative to using * StringTokenizer. * A {@code null} input String returns {@code null}. A {@code null} separatorChars splits on * whitespace. * * {{{ * split(null, *) = null * split("", *) = [] * split("abc def", null) = ["abc", "def"] * split("abc def", " ") = ["abc", "def"] * split("abc def", " ") = ["abc", "def"] * split("ab:cd:ef", ":") = ["ab", "cd", "ef"] * }}} */ def split(str: String, separatorChars: String): Array[String] = { if (str == null) return null val len = str.length if (len == 0) return new Array[String](0) val list = new mutable.ListBuffer[String] var i, start = 0 var matched = false val sepChars = if (null == separatorChars) " " else separatorChars while (i < len) { if (sepChars.indexOf(str.charAt(i)) >= 0) { if (matched) { list += str.substring(start, i) matched = false } start = i + 1 } else matched = true i += 1 } if (matched) list += str.substring(start, i) list.toArray } /** 将1-2,3,4-9之类的序列拆分成数组 * * @param numSeq a String object. * @return an array of Int objects. */ def splitNumSeq(numSeq: String): Array[Int] = { if (isEmpty(numSeq)) return null val numArray = split(numSeq, ',') val numSet = new mutable.HashSet[Int] (0 until numArray.length) foreach { i => val num = numArray(i) if (num.contains("-")) { val termFromTo = split(num, '-') val from = Numbers.toInt(termFromTo(0)) val to = Numbers.toInt(termFromTo(1)) var j = from while (j <= to) { numSet.add(j) j += 1 } } else numSet.add(Numbers.toInt(num)) } numSet.toArray } /** splitToInteger. */ def splitToInt(ids: String): Seq[Int] = if (isEmpty(ids)) List.empty else Numbers.toInt(split(ids, ',')).toIndexedSeq /** splitToLong. * */ def splitToLong(ids: String): Seq[Long] = if (isEmpty(ids)) List.empty else Numbers.toLong(split(ids, ',')).toIndexedSeq /** Gets a substring from the specified String avoiding exceptions. * * A negative start position can be used to start/end {@code n} characters from the end of the * String. * The returned substring starts with the character in the {@code start} position and ends before * the {@code end} position. All position counting is zero-based -- i.e., to start at the * beginning of the string use {@code start = 0}. Negative start and end positions can be used to * specify offsets relative to the end of the String. * If {@code start} is not strictly to the left of {@code end}, "" is returned. * * {{{ * substring(null, *, *) = null * substring("", * , *) = ""; * substring("abc", 0, 2) = "ab" * substring("abc", 2, 0) = "" * substring("abc", 2, 4) = "c" * substring("abc", 4, 6) = "" * substring("abc", 2, 2) = "" * substring("abc", -2, -1) = "b" * substring("abc", -4, 2) = "ab" * }}} * * @param str the String to get the substring from, may be null * @param startIndex the position to start from, negative means * count back from the end of the String by this many characters * @param endIndex the position to end at (exclusive), negative means * count back from the end of the String by this many characters * @return substring from start position to end position, { @code null} if null String input */ def substring(str: String, startIndex: Int, endIndex: Int): String = { if (str == null) return null var start = startIndex var end = endIndex if (start < 0) start = str.length + start if (end < 0) end = str.length + end if (end > str.length) end = str.length if (start > end) return "" if (start < 0) start = 0 if (end < 0) end = 0 str.substring(start, end) } /** subtractSeq. * * @param first a String object. * @param second a String object. * @return a String object. */ def subtractSeq(first: String, second: String): String = subtractSeq(first, second, DELIMITER) /** 返回一个新的逗号相隔字符串,实现其中的单词a-b的功能. 新的字符串将以,开始,结束
*/ def subtractSeq(first: String, second: String, delimiter: String): String = { if (isEmpty(first)) return "" if (isEmpty(second)) { val builder = new StringBuilder() if (!first.startsWith(delimiter)) builder.append(delimiter).append(first) if (!first.endsWith(delimiter)) builder.append(first).append(delimiter) return builder.toString } val firstSeq = split(first, delimiter).toList val secondSeq = split(second, delimiter).toList val rs = Collections.subtract(firstSeq, secondSeq) val buf = new StringBuilder() rs foreach { ele => buf.append(delimiter).append(ele) } if (buf.length > 0) buf.append(delimiter) buf.toString } /** unCamel. */ def unCamel(str: String): String = unCamel(str, '-', true) /** unCamel. * * @param str a String object. * @param seperator a char. * @return a String object. */ def unCamel(str: String, seperator: Char): String = unCamel(str, seperator, true) /** 将驼峰表示法转换为下划线小写表示 * * @param str a String object. * @param seperator a char. * @param lowercase a boolean. * @return a String object. */ def unCamel(str: String, seperator: Char, lowercase: Boolean): String = { if (3 > str.length) return if (lowercase) str.toLowerCase else str val ca = str.toCharArray() val build = new StringBuilder(ca.length + 5) build.append(if (lowercase) toLowerCase(ca(0)) else ca(0)) var lower1 = isLower(ca(0)) var i = 1 while (i < ca.length - 1) { val cur = ca(i) val next = ca(i + 1) val upper2 = isUpper(cur) val lower3 = isLower(next) if (lower1 && upper2 && lower3) { build.append(seperator) build.append(if (lowercase) toLowerCase(cur) else cur) build.append(next) i += 2 } else { if (lowercase && upper2) build.append(toLowerCase(cur)) else build.append(cur) lower1 = !upper2 i += 1 } } if (i == ca.length - 1) { if (isLower(ca(i - 1)) && isUpper(ca(i))) build.append(seperator) build.append(if (lowercase) toLowerCase(ca(i)) else ca(i)) } build.toString } /** Gets the substring before the first occurrence of a separator. The separator is not returned. * A {@code null} string input will return {@code null}. An empty ("") string input will return * the empty string. A {@code null} separator will return the input string. * If nothing is found, the string input is returned. * * {{{ * substringBefore(null, *) = null * substringBefore("", *) = "" * substringBefore("abc", "a") = "" * substringBefore("abcba", "b") = "a" * substringBefore("abc", "c") = "ab" * substringBefore("abc", "d") = "abc" * substringBefore("abc", "") = "" * substringBefore("abc", null) = "abc" * }}} * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring before the first occurrence of the separator, { @code null} if null String * input * @since 2.0 */ def substringBefore(str: String, separator: String): String = { if (isEmpty(str) || separator == null) return str if (separator.length == 0) return Empty val pos = str.indexOf(separator) if (pos == Index_not_found) return str str.substring(0, pos) } /** Gets the substring after the first occurrence of a separator. The separator is not returned. * A {@code null} string input will return {@code null}. An empty ("") string input will return * the empty string. A {@code null} separator will return the empty string if the input string is * not {@code null}. * If nothing is found, the empty string is returned. * * {{{ * substringAfter(null, *) = null * substringAfter("", *) = "" * substringAfter(*, null) = "" * substringAfter("abc", "a") = "bc" * substringAfter("abcba", "b") = "cba" * substringAfter("abc", "c") = "" * substringAfter("abc", "d") = "" * substringAfter("abc", "") = "abc" * }}} * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring after the first occurrence of the separator, { @code null} if null String * input * @since 2.0 */ def substringAfter(str: String, separator: String): String = { if (isEmpty(str)) return str if (separator == null) return Empty val pos = str.indexOf(separator) if (pos == Index_not_found) return Empty str.substring(pos + separator.length) } /** Gets the String that is nested in between two Strings. Only the first match is returned. * A {@code null} input String returns {@code null}. A {@code null} open/close returns * {@code null} (no match). An empty ("") open and close returns an empty string. * * {{{ * substringBetween("wx[b]yz", "[", "]") = "b" * substringBetween(null, *, *) = null * substringBetween(*, null, *) = null * substringBetween(*, *, null) = null * substringBetween("", "", "") = "" * substringBetween("", "", "]") = null * substringBetween("", "[", "]") = null * substringBetween("yabcz", "", "") = "" * substringBetween("yabcz", "y", "z") = "abc" * substringBetween("yabczyabcz", "y", "z") = "abc" * }}} * * @param str the String containing the substring, may be null * @param open the String before the substring, may be null * @param close the String after the substring, may be null * @return the substring, { @code null} if no match * @since 3.0 */ def substringBetween(str: String, open: String, close: String): String = { if (str == null || open == null || close == null) return null val start = str.indexOf(open) if (start != Index_not_found) { val end = str.indexOf(close, start + open.length) if (end != Index_not_found) return str.substring(start + open.length, end) } null } /** Gets the substring before the last occurrence of a separator. The separator is not returned. * A {@code null} string input will return {@code null}. An empty ("") string input will return * the empty string. An empty or {@code null} separator will return the input string. * If nothing is found, the string input is returned. * * {{{ * substringBeforeLast(null, *) = null * substringBeforeLast("", *) = "" * substringBeforeLast("abcba", "b") = "abc" * substringBeforeLast("abc", "c") = "ab" * substringBeforeLast("a", "a") = "" * substringBeforeLast("a", "z") = "a" * substringBeforeLast("a", null) = "a" * substringBeforeLast("a", "") = "a" * }}} * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring before the last occurrence of the separator, { @code null} if null String * input * @since 3.0 */ def substringBeforeLast(str: String, separator: String): String = { if (isEmpty(str) || isEmpty(separator)) return str val pos = str.lastIndexOf(separator) if (pos == Index_not_found) return str str.substring(0, pos) } /** Gets the substring after the last occurrence of a separator. The separator is not returned. * A {@code null} string input will return {@code null}. An empty ("") string input will return * the empty string. An empty or {@code null} separator will return the empty string if the input * string is not {@code null}. * If nothing is found, the empty string is returned. * * {{{ * substringAfterLast(null, *) = null * substringAfterLast("", *) = "" * substringAfterLast(*, "") = "" * substringAfterLast(*, null) = "" * substringAfterLast("abc", "a") = "bc" * substringAfterLast("abcba", "b") = "a" * substringAfterLast("abc", "c") = "" * substringAfterLast("a", "a") = "" * substringAfterLast("a", "z") = "" * }}} * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring after the last occurrence of the separator, { @code null} if null String * input * @since 3.0 */ def substringAfterLast(str: String, separator: String): String = { if (isEmpty(str)) return str if (isEmpty(separator)) return Empty val pos = str.lastIndexOf(separator) if (pos == Index_not_found || pos == str.length - separator.length) return Empty str.substring(pos + separator.length) } /** Removes control characters (char <= 32) from both ends of this String, handling {@code null} * by returning {@code null}. * The String is trimmed using {@link String# trim ( )}. Trim removes start and end characters <= * 32. * * {{{ * trim(null) = null * trim("") = "" * trim(" ") = "" * trim("abc") = "abc" * trim(" abc ") = "abc" * }}} * * @param str the String to be trimmed, may be null * @return the trimmed string, { @code null} if null String input * @since 3.0 */ def trim(str: String): String = if (str == null) null else str.trim() def trimEnd(str: String): String = if (str == null) null else { var len = str.length while (len > 0 && (str.charAt(len - 1) <= ' ')) len -= 1 if (len < str.length) str.substring(0, len) else str } /** Strips any of a set of characters from the end of a String. * *

A null input String returns null. * An empty string ("") input returns the empty string.

* *

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

* * {{{ * stripEnd(null, *) = null * stripEnd("", *) = "" * stripEnd("abc", "") = "abc" * stripEnd(*, null) = * * stripEnd(" abcyx", "xyz") = " abc" * }}} * * @param str the String to remove characters from, may be null * @param stripChars the characters to remove, null treated as whitespace * @return the stripped String, null if null String input */ def stripEnd(str: String, stripChars: String): String = if (str == null || str.length == 0 || null == stripChars || stripChars.length == 0) str else { var end = str.length while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != -1)) end -= 1 str.substring(0, end) } /** Uncapitalizes a String changing the first letter to title case as per * [[java.lang.Character#toLowerCase(char)]]. No other letters are changed. * For a word based algorithm, see String returns {@code null}. * * {{{ * uncapitalize(null) = null * uncapitalize("") = "" * uncapitalize("Cat") = "cat" * uncapitalize("CAT") = "cAT" * }}} * * @param str the String to uncapitalize, may be null * @return the uncapitalized String, { @code null} if null String input * @see #capitalize(String) * @since 3.0 */ def uncapitalize(str: String): String = { if ((str eq null) || str.length == 0) return str val head = str.charAt(0) val lower = Character.toLowerCase(head) if (lower == head) str else { val chars = str.toCharArray chars(0) = lower new String(chars) } } /** Returns either the passed in CharSequence, or if the CharSequence is whitespace, empty ("") or * {@code null}, the value of {@code defaultStr}. * * @param str * @param defaultStr */ def defaultIfBlank[T <: CharSequence](str: T, defaultStr: T): T = if (isBlank(str)) defaultStr else str def lowerCase(s: String): String = if (null == s) null else s.toLowerCase() def upperCase(s: String): String = if (null == s) null else s.toUpperCase() def abbreviate(str: String, maxWidth: Int): String = abbreviate(str, 0, maxWidth); def abbreviate(str: String, offset: Int, maxWidth: Int): String = { if (str eq null) return null if (maxWidth < 4) throw new IllegalArgumentException("Minimum abbreviation width is 4") if (str.length <= maxWidth) return str var newoffset = offset if (newoffset > str.length()) newoffset = str.length if (str.length() - newoffset < maxWidth - 3) newoffset = str.length() - (maxWidth - 3) var abrevMarker = "..." if (newoffset <= 4) return str.substring(0, maxWidth - 3) + abrevMarker if (maxWidth < 7) throw new IllegalArgumentException("Minimum abbreviation width with offset is 7") if (newoffset + maxWidth - 3 < str.length) abrevMarker + abbreviate(str.substring(newoffset), maxWidth - 3) else abrevMarker + str.substring(str.length() - (maxWidth - 3)) } /** Removes all occurrences of a character from within the source string. * A {@code null} source string will return {@code null}. An empty ("") source string will return * the empty string. * * {{{ * StringUtils.remove(null, *) = null * StringUtils.remove("", *) = "" * StringUtils.remove("queued", 'u') = "qeed" * StringUtils.remove("queued", 'z') = "queued" * }}} * * @param str the source String to search, may be null * @param remove the char to search for and remove, may be null * @return the substring with the char removed if found, { @code null} if null String input */ def remove(str: String, remove: Char): String = { if (isEmpty(str) || str.indexOf(remove) == -1) return str val chars = str.toCharArray var i, pos = 0 while (i < chars.length) { if (chars(i) != remove) { chars(pos) = chars(i) pos += 1 } i += 1 } new String(chars, 0, pos); } def format(format: String, args: Any*): String = new java.util.Formatter().format(format, args.toArray.asInstanceOf[Array[Object]]: _*).toString }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy