001// Generated by delombok at Sat Feb 25 17:58:34 UTC 2023 002package io.cui.tools.string; 003 004import static io.cui.tools.base.Preconditions.checkArgument; 005import java.util.Optional; 006import java.util.function.Predicate; 007import io.cui.tools.logging.CuiLogger; 008import lombok.NonNull; 009 010/** 011 * Provides some extensions to String handling like com.google.common.base.Strings. It was 012 * created using different source, see authors 013 * 014 * @author Oliver Wolff 015 * @author Sven Haag 016 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 017 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java 018 * @author https://github.com/spring-projects/spring-framework/blob/v5.1.8.RELEASE/spring-core/src/main/java/org/springframework/util/StringUtils.java 019 * @author com.google.common.base.Strings 020 */ 021public final class MoreStrings { 022 private static final CuiLogger log = new CuiLogger(MoreStrings.class); 023 /** 024 * The empty String {@code ""}. 025 */ 026 public static final String EMPTY = ""; 027 /** 028 * <p> 029 * The maximum size to which the padding constant(s) can expand. 030 * </p> 031 */ 032 private static final int PAD_LIMIT = 8192; 033 /** 034 * A String for a space character. 035 */ 036 public static final String SPACE = " "; 037 /** 038 * Represents a failed index search. 039 */ 040 public static final int INDEX_NOT_FOUND = -1; 041 042 /** 043 * "Unquotes" a String, saying if the given String starts and ends with the token "'" or """ the 044 * quotes will be stripped 045 * 046 * @param original may be null or empty 047 * @return the unquoted String or the original in none could be found 048 */ 049 public static String unquote(final String original) { 050 if (isEmpty(original)) { 051 return original; 052 } 053 if (original.startsWith("\"") && original.endsWith("\"") || original.startsWith("\'") && original.endsWith("\'")) { 054 return original.substring(1, original.length() - 1); 055 } 056 return original; 057 } 058 059 /** 060 * <p> 061 * Checks if the CharSequence contains only lowercase characters. 062 * </p> 063 * 064 * <p> 065 * {@code null} will return {@code false}. 066 * An empty CharSequence (length()=0) will return {@code false}. 067 * </p> 068 * 069 * <pre> 070 * MoreStrings.isAllLowerCase(null) = false 071 * MoreStrings.isAllLowerCase("") = false 072 * MoreStrings.isAllLowerCase(" ") = false 073 * MoreStrings.isAllLowerCase("abc") = true 074 * MoreStrings.isAllLowerCase("abC") = false 075 * MoreStrings.isAllLowerCase("ab c") = false 076 * MoreStrings.isAllLowerCase("ab1c") = false 077 * MoreStrings.isAllLowerCase("ab/c") = false 078 * </pre> 079 * 080 * @param cs the CharSequence to check, may be null 081 * 082 * @return {@code true} if only contains lowercase characters, and is non-null 083 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 084 */ 085 public static boolean isAllLowerCase(final CharSequence cs) { 086 if (isEmpty(cs)) { 087 return false; 088 } 089 final var sz = cs.length(); 090 for (var i = 0; i < sz; i++) { 091 if (!Character.isLowerCase(cs.charAt(i))) { 092 return false; 093 } 094 } 095 return true; 096 } 097 098 /** 099 * <p> 100 * Checks if the CharSequence contains only uppercase characters. 101 * </p> 102 * 103 * <p> 104 * {@code null} will return {@code false}. 105 * An empty String (length()=0) will return {@code false}. 106 * </p> 107 * 108 * <pre> 109 * MoreStrings.isAllUpperCase(null) = false 110 * MoreStrings.isAllUpperCase("") = false 111 * MoreStrings.isAllUpperCase(" ") = false 112 * MoreStrings.isAllUpperCase("ABC") = true 113 * MoreStrings.isAllUpperCase("aBC") = false 114 * MoreStrings.isAllUpperCase("A C") = false 115 * MoreStrings.isAllUpperCase("A1C") = false 116 * MoreStrings.isAllUpperCase("A/C") = false 117 * </pre> 118 * 119 * @param cs the CharSequence to check, may be null 120 * 121 * @return {@code true} if only contains uppercase characters, and is non-null 122 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 123 */ 124 public static boolean isAllUpperCase(final CharSequence cs) { 125 if (isEmpty(cs)) { 126 return false; 127 } 128 final var sz = cs.length(); 129 for (var i = 0; i < sz; i++) { 130 if (!Character.isUpperCase(cs.charAt(i))) { 131 return false; 132 } 133 } 134 return true; 135 } 136 137 /** 138 * <p> 139 * Checks if a CharSequence is empty ("") or null. 140 * </p> 141 * 142 * <pre> 143 * MoreStrings.isEmpty(null) = true 144 * MoreStrings.isEmpty("") = true 145 * MoreStrings.isEmpty(" ") = false 146 * MoreStrings.isEmpty("bob") = false 147 * MoreStrings.isEmpty(" bob ") = false 148 * </pre> 149 * 150 * @param cs the CharSequence to check, may be null 151 * 152 * @return {@code true} if the CharSequence is empty or null 153 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 154 */ 155 public static boolean isEmpty(final CharSequence cs) { 156 return cs == null || cs.length() == 0; 157 } 158 159 /** 160 * <p> 161 * Checks if a CharSequence is not empty ("") or null. 162 * </p> 163 * 164 * <pre> 165 * MoreStrings.isEmpty(null) = false 166 * MoreStrings.isEmpty("") = false 167 * MoreStrings.isEmpty(" ") = true 168 * </pre> 169 * 170 * @param cs the CharSequence to check, may be null 171 * 172 * @return {@code true} if the CharSequence is not empty 173 */ 174 public static boolean isPresent(final CharSequence cs) { 175 return cs != null && cs.length() > 0; 176 } 177 178 /** 179 * <p> 180 * Checks if the CharSequence contains only Unicode digits. 181 * A decimal point is not a Unicode digit and returns false. 182 * </p> 183 * 184 * <p> 185 * {@code null} will return {@code false}. 186 * An empty CharSequence (length()=0) will return {@code false}. 187 * </p> 188 * 189 * <p> 190 * Note that the method does not allow for a leading sign, either positive or negative. 191 * Also, if a String passes the numeric test, it may still generate a NumberFormatException 192 * when parsed by Integer.parseInt or Long.parseLong, e.g. if the value is outside the range 193 * for int or long respectively. 194 * </p> 195 * 196 * <pre> 197 * MoreStrings.isNumeric(null) = false 198 * MoreStrings.isNumeric("") = false 199 * MoreStrings.isNumeric(" ") = false 200 * MoreStrings.isNumeric("123") = true 201 * MoreStrings.isNumeric("\u0967\u0968\u0969") = true 202 * MoreStrings.isNumeric("12 3") = false 203 * MoreStrings.isNumeric("ab2c") = false 204 * MoreStrings.isNumeric("12-3") = false 205 * MoreStrings.isNumeric("12.3") = false 206 * MoreStrings.isNumeric("-123") = false 207 * MoreStrings.isNumeric("+123") = false 208 * </pre> 209 * 210 * @param cs the CharSequence to check, may be null 211 * 212 * @return {@code true} if only contains digits, and is non-null 213 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 214 */ 215 public static boolean isNumeric(final CharSequence cs) { 216 if (isEmpty(cs)) { 217 return false; 218 } 219 final var sz = cs.length(); 220 for (var i = 0; i < sz; i++) { 221 if (!Character.isDigit(cs.charAt(i))) { 222 return false; 223 } 224 } 225 return true; 226 } 227 228 /** 229 * <p> 230 * Checks if a CharSequence is empty (""), null or whitespace only. 231 * </p> 232 * 233 * <p> 234 * Whitespace is defined by {@link Character#isWhitespace(char)}. 235 * </p> 236 * 237 * <pre> 238 * MoreStrings.isBlank(null) = true 239 * MoreStrings.isBlank("") = true 240 * MoreStrings.isBlank(" ") = true 241 * MoreStrings.isBlank("bob") = false 242 * MoreStrings.isBlank(" bob ") = false 243 * </pre> 244 * 245 * @param cs the CharSequence to check, may be null 246 * 247 * @return {@code true} if the CharSequence is null, empty or whitespace only 248 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 249 */ 250 public static boolean isBlank(final CharSequence cs) { 251 final int strLen; 252 if (cs == null || (strLen = cs.length()) == 0) { 253 return true; 254 } 255 for (var i = 0; i < strLen; i++) { 256 if (!Character.isWhitespace(cs.charAt(i))) { 257 return false; 258 } 259 } 260 return true; 261 } 262 263 /** 264 * {@code NOT} of {@link #isBlank(CharSequence)}. 265 * 266 * <p> 267 * Checks if a CharSequence is not empty (""), null or contains whitespaces only. 268 * </p> 269 * 270 * <p> 271 * Whitespace is defined by {@link Character#isWhitespace(char)}. 272 * </p> 273 * 274 * <pre> 275 * MoreStrings.isBlank(null) = false 276 * MoreStrings.isBlank("") = false 277 * MoreStrings.isBlank(" ") = false 278 * MoreStrings.isBlank("bob") = true 279 * MoreStrings.isBlank(" bob ") = true 280 * </pre> 281 * 282 * @param cs to be checked 283 * @return {@code true} if the given string is no blank, {@code false} otherwise 284 */ 285 public static boolean isNotBlank(final CharSequence cs) { 286 return !isBlank(cs); 287 } 288 289 /** 290 * <p> 291 * Counts how many times the substring appears in the larger string. 292 * </p> 293 * 294 * <p> 295 * A {@code null} or empty ("") String input returns {@code 0}. 296 * </p> 297 * 298 * <pre> 299 * MoreStrings.countMatches(null, *) = 0 300 * MoreStrings.countMatches("", *) = 0 301 * MoreStrings.countMatches("abba", null) = 0 302 * MoreStrings.countMatches("abba", "") = 0 303 * MoreStrings.countMatches("abba", "a") = 2 304 * MoreStrings.countMatches("abba", "ab") = 1 305 * MoreStrings.countMatches("abba", "xxx") = 0 306 * </pre> 307 * 308 * 309 * @param str the CharSequence to check, may be null 310 * @param sub the substring to count, may be null 311 * @return the number of occurrences, 0 if either CharSequence is {@code null} 312 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 313 */ 314 public static int countMatches(final CharSequence str, final CharSequence sub) { 315 if (isEmpty(str) || isEmpty(sub)) { 316 return 0; 317 } 318 var count = 0; 319 var idx = 0; 320 while ((idx = indexOf(str, sub, idx)) != INDEX_NOT_FOUND) { 321 count++; 322 idx += sub.length(); 323 } 324 return count; 325 } 326 327 /** 328 * <p> 329 * Left pad a String with spaces (' '). 330 * </p> 331 * 332 * <p> 333 * The String is padded to the size of {@code size}. 334 * </p> 335 * 336 * <pre> 337 * MoreStrings.leftPad(null, *) = null 338 * MoreStrings.leftPad("", 3) = " " 339 * MoreStrings.leftPad("bat", 3) = "bat" 340 * MoreStrings.leftPad("bat", 5) = " bat" 341 * MoreStrings.leftPad("bat", 1) = "bat" 342 * MoreStrings.leftPad("bat", -1) = "bat" 343 * </pre> 344 * 345 * @param str the String to pad out, may be null 346 * @param size the size to pad to 347 * @return left padded String or original String if no padding is necessary, 348 * {@code null} if null String input 349 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 350 */ 351 public static String leftPad(final String str, final int size) { 352 return leftPad(str, size, ' '); 353 } 354 355 /** 356 * <p> 357 * Left pad a String with a specified character. 358 * </p> 359 * 360 * <p> 361 * Pad to a size of {@code size}. 362 * </p> 363 * 364 * <pre> 365 * MoreStrings.leftPad(null, *, *) = null 366 * MoreStrings.leftPad("", 3, 'z') = "zzz" 367 * MoreStrings.leftPad("bat", 3, 'z') = "bat" 368 * MoreStrings.leftPad("bat", 5, 'z') = "zzbat" 369 * MoreStrings.leftPad("bat", 1, 'z') = "bat" 370 * MoreStrings.leftPad("bat", -1, 'z') = "bat" 371 * </pre> 372 * 373 * @param str the String to pad out, may be null 374 * @param size the size to pad to 375 * @param padChar the character to pad with 376 * @return left padded String or original String if no padding is necessary, 377 * {@code null} if null String input 378 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 379 */ 380 public static String leftPad(final String str, final int size, final char padChar) { 381 if (str == null) { 382 return null; 383 } 384 final var pads = size - str.length(); 385 if (pads <= 0) { 386 return str; // returns original String when possible 387 } 388 if (pads > PAD_LIMIT) { 389 return leftPad(str, size, String.valueOf(padChar)); 390 } 391 return repeat(padChar, pads).concat(str); 392 } 393 394 /** 395 * <p> 396 * Left pad a String with a specified String. 397 * </p> 398 * 399 * <p> 400 * Pad to a size of {@code size}. 401 * </p> 402 * 403 * <pre> 404 * MoreStrings.leftPad(null, *, *) = null 405 * MoreStrings.leftPad("", 3, "z") = "zzz" 406 * MoreStrings.leftPad("bat", 3, "yz") = "bat" 407 * MoreStrings.leftPad("bat", 5, "yz") = "yzbat" 408 * MoreStrings.leftPad("bat", 8, "yz") = "yzyzybat" 409 * MoreStrings.leftPad("bat", 1, "yz") = "bat" 410 * MoreStrings.leftPad("bat", -1, "yz") = "bat" 411 * MoreStrings.leftPad("bat", 5, null) = " bat" 412 * MoreStrings.leftPad("bat", 5, "") = " bat" 413 * </pre> 414 * 415 * @param str the String to pad out, may be null 416 * @param size the size to pad to 417 * @param padStr the String to pad with, null or empty treated as single space 418 * @return left padded String or original String if no padding is necessary, 419 * {@code null} if null String input 420 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 421 */ 422 public static String leftPad(final String str, final int size, String padStr) { 423 if (str == null) { 424 return null; 425 } 426 if (isEmpty(padStr)) { 427 padStr = SPACE; 428 } 429 final var padLen = padStr.length(); 430 final var strLen = str.length(); 431 final var pads = size - strLen; 432 if (pads <= 0) { 433 return str; // returns original String when possible 434 } 435 if (padLen == 1 && pads <= PAD_LIMIT) { 436 return leftPad(str, size, padStr.charAt(0)); 437 } 438 if (pads == padLen) { 439 return padStr.concat(str); 440 } 441 if (pads < padLen) { 442 return padStr.substring(0, pads).concat(str); 443 } 444 final var padding = new char[pads]; 445 final var padChars = padStr.toCharArray(); 446 for (var i = 0; i < pads; i++) { 447 padding[i] = padChars[i % padLen]; 448 } 449 return new String(padding).concat(str); 450 } 451 452 /** 453 * <p> 454 * Returns padding using the specified delimiter repeated 455 * to a given length. 456 * </p> 457 * 458 * <pre> 459 * MoreStrings.repeat('e', 0) = "" 460 * MoreStrings.repeat('e', 3) = "eee" 461 * MoreStrings.repeat('e', -2) = "" 462 * </pre> 463 * 464 * <p> 465 * Note: this method does not support padding with 466 * <a href="http://www.unicode.org/glossary/#supplementary_character">Unicode Supplementary 467 * Characters</a> 468 * as they require a pair of {@code char}s to be represented. 469 * If you are needing to support full I18N of your applications 470 * consider using <code>repeat(String, int)</code> instead. 471 * </p> 472 * 473 * @param ch character to repeat 474 * @param repeat number of times to repeat char, negative treated as zero 475 * @return String with repeated character 476 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 477 */ 478 public static String repeat(final char ch, final int repeat) { 479 if (repeat <= 0) { 480 return EMPTY; 481 } 482 final var buf = new char[repeat]; 483 for (var i = repeat - 1; i >= 0; i--) { 484 buf[i] = ch; 485 } 486 return new String(buf); 487 } 488 489 /** 490 * <p> 491 * Strips any of a set of characters from the end of a String. 492 * </p> 493 * 494 * <p> 495 * A {@code null} input String returns {@code null}. 496 * An empty string ("") input returns the empty string. 497 * </p> 498 * 499 * <p> 500 * If the stripChars String is {@code null}, whitespace is 501 * stripped as defined by {@link Character#isWhitespace(char)}. 502 * </p> 503 * 504 * <pre> 505 * MoreStrings.stripEnd(null, *) = null 506 * MoreStrings.stripEnd("", *) = "" 507 * MoreStrings.stripEnd("abc", "") = "abc" 508 * MoreStrings.stripEnd("abc", null) = "abc" 509 * MoreStrings.stripEnd(" abc", null) = " abc" 510 * MoreStrings.stripEnd("abc ", null) = "abc" 511 * MoreStrings.stripEnd(" abc ", null) = " abc" 512 * MoreStrings.stripEnd(" abcyx", "xyz") = " abc" 513 * MoreStrings.stripEnd("120.00", ".0") = "12" 514 * </pre> 515 * 516 * 517 * @param str the String to remove characters from, may be null 518 * @param stripChars the set of characters to remove, null treated as whitespace 519 * @return the stripped String, {@code null} if null String input 520 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 521 */ 522 public static String stripEnd(final String str, final String stripChars) { 523 int end; 524 if (str == null || (end = str.length()) == 0) { 525 return str; 526 } 527 if (stripChars == null) { 528 while (end != 0 && Character.isWhitespace(str.charAt(end - 1))) { 529 end--; 530 } 531 } else if (stripChars.isEmpty()) { 532 return str; 533 } else { 534 while (end != 0 && stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND) { 535 end--; 536 } 537 } 538 return str.substring(0, end); 539 } 540 541 /** 542 * Returns the index within <code>seq</code> of the first occurrence of 543 * the specified character. If a character with value 544 * <code>searchChar</code> occurs in the character sequence represented by 545 * <code>seq</code> <code>CharSequence</code> object, then the index (in Unicode 546 * code units) of the first such occurrence is returned. For 547 * values of <code>searchChar</code> in the range from 0 to 0xFFFF 548 * (inclusive), this is the smallest value <i>k</i> such that: 549 * <blockquote> 550 * 551 * <pre> 552 * this.charAt(<i>k</i>) == searchChar 553 * </pre> 554 * 555 * </blockquote> 556 * is true. For other values of <code>searchChar</code>, it is the 557 * smallest value <i>k</i> such that: 558 * <blockquote> 559 * 560 * <pre> 561 * this.codePointAt(<i>k</i>) == searchChar 562 * </pre> 563 * 564 * </blockquote> 565 * is true. In either case, if no such character occurs in <code>seq</code>, 566 * then {@code INDEX_NOT_FOUND (-1)} is returned. 567 * 568 * <p> 569 * Furthermore, a {@code null} or empty ("") CharSequence will 570 * return {@code INDEX_NOT_FOUND (-1)}. 571 * </p> 572 * 573 * <pre> 574 * MoreStrings.indexOf(null, *) = -1 575 * MoreStrings.indexOf("", *) = -1 576 * MoreStrings.indexOf("aabaabaa", 'a') = 0 577 * MoreStrings.indexOf("aabaabaa", 'b') = 2 578 * </pre> 579 * 580 * 581 * @param seq the CharSequence to check, may be null 582 * @param searchChar the character to find 583 * @return the first index of the search character, 584 * -1 if no match or {@code null} string input 585 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 586 */ 587 public static int indexOf(final CharSequence seq, final int searchChar) { 588 if (isEmpty(seq)) { 589 return INDEX_NOT_FOUND; 590 } 591 return indexOf(seq, searchChar, 0); 592 } 593 594 // owolff: original code 595 /** 596 * Returns the index within <code>cs</code> of the first occurrence of the 597 * specified character, starting the search at the specified index. 598 * <p> 599 * If a character with value <code>searchChar</code> occurs in the 600 * character sequence represented by the <code>cs</code> 601 * object at an index no smaller than <code>start</code>, then 602 * the index of the first such occurrence is returned. For values 603 * of <code>searchChar</code> in the range from 0 to 0xFFFF (inclusive), 604 * this is the smallest value <i>k</i> such that: 605 * <blockquote> 606 * 607 * <pre> 608 * (this.charAt(<i>k</i>) == searchChar) && (<i>k</i> >= start) 609 * </pre> 610 * 611 * </blockquote> 612 * is true. For other values of <code>searchChar</code>, it is the 613 * smallest value <i>k</i> such that: 614 * <blockquote> 615 * 616 * <pre> 617 * (this.codePointAt(<i>k</i>) == searchChar) && (<i>k</i> >= start) 618 * </pre> 619 * 620 * </blockquote> 621 * is true. In either case, if no such character occurs inm <code>cs</code> 622 * at or after position <code>start</code>, then 623 * <code>-1</code> is returned. 624 * 625 * <p> 626 * There is no restriction on the value of <code>start</code>. If it 627 * is negative, it has the same effect as if it were zero: the entire 628 * <code>CharSequence</code> may be searched. If it is greater than 629 * the length of <code>cs</code>, it has the same effect as if it were 630 * equal to the length of <code>cs</code>: <code>-1</code> is returned. 631 * 632 * <p> 633 * All indices are specified in <code>char</code> values 634 * (Unicode code units). 635 * 636 * @param cs the {@code CharSequence} to be processed, not null 637 * @param searchChar the char to be searched for 638 * @param start the start index, negative starts at the string start 639 * @return the index where the search char was found, -1 if not found 640 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 641 */ 642 @SuppressWarnings("squid:S3776") 643 public static int indexOf(final CharSequence cs, final int searchChar, int start) { 644 if (isEmpty(cs)) { 645 return INDEX_NOT_FOUND; 646 } 647 if (cs instanceof String) { 648 return ((String) cs).indexOf(searchChar, start); 649 } 650 final var sz = cs.length(); 651 if (start < 0) { 652 start = 0; 653 } 654 if (searchChar < Character.MIN_SUPPLEMENTARY_CODE_POINT) { 655 for (var i = start; i < sz; i++) { 656 if (cs.charAt(i) == searchChar) { 657 return i; 658 } 659 } 660 } 661 // supplementary characters (LANG1300) 662 if (searchChar <= Character.MAX_CODE_POINT) { 663 final var chars = Character.toChars(searchChar); 664 for (var i = start; i < sz - 1; i++) { 665 final var high = cs.charAt(i); 666 final var low = cs.charAt(i + 1); 667 if (high == chars[0] && low == chars[1]) { 668 return i; 669 } 670 } 671 } 672 return INDEX_NOT_FOUND; 673 } 674 675 /** 676 * Used by the indexOf(CharSequence methods) as a green implementation of indexOf. 677 * 678 * @param cs the {@code CharSequence} to be processed 679 * @param searchChar the {@code CharSequence} to be searched for 680 * @param start the start index 681 * @return the index where the search sequence was found 682 * 683 * @author https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java 684 */ 685 public static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) { 686 if (cs == null || searchChar == null) { 687 return INDEX_NOT_FOUND; 688 } 689 return cs.toString().indexOf(searchChar.toString(), start); 690 } 691 692 /** 693 * Check whether the given {@code String} contains actual <em>text</em>. 694 * <p> 695 * More specifically, this method returns {@code true} if the 696 * {@code String} is not {@code null}, its length is greater than 0, 697 * and it contains at least one non-whitespace character. 698 * 699 * @param str the {@code String} to check (maybe {@code null}) 700 * 701 * @return {@code true} if the {@code String} is not {@code null}, its 702 * length is greater than 0, and it does not contain whitespace only 703 * @author https://github.com/spring-projects/spring-framework/blob/v5.1.8.RELEASE/spring-core/src/main/java/org/springframework/util/StringUtils.java 704 */ 705 public static boolean hasNonWhitespaceChar(final CharSequence str) { 706 if (isEmpty(str)) { 707 return false; 708 } 709 final var strLen = str.length(); 710 for (var i = 0; i < strLen; i++) { 711 if (!Character.isWhitespace(str.charAt(i))) { 712 return true; 713 } 714 } 715 return false; 716 } 717 718 /** 719 * Checks a string for being non-null and not empty (checks without trimming) Throws an 720 * {@link IllegalArgumentException} if String is null or empty 721 * 722 * @param underCheck 723 * @return the given String 724 */ 725 public static String requireNotEmpty(final String underCheck) { 726 checkArgument(!isEmpty(underCheck), "Given String is Empty"); 727 return underCheck; 728 } 729 730 /** 731 * Checks a string for being non-null and not empty (checks without trimming) Throws an 732 * {@link IllegalArgumentException} if String is null or empty 733 * 734 * @param underCheck 735 * @param attributeName used for the creation of the error text 736 * @return the given String 737 */ 738 public static String requireNotEmpty(final String underCheck, final String attributeName) { 739 checkArgument(!isEmpty(underCheck), "Attribute with name \'" + attributeName + "\' must not be empty"); 740 return underCheck; 741 } 742 743 /** 744 * Checks a string for being non-null and not empty (checks with trimming) Throws an 745 * {@link IllegalArgumentException} if String is null or empty 746 * 747 * @param underCheck 748 * @return the given String 749 */ 750 public static String requireNotEmptyTrimmed(final String underCheck) { 751 checkArgument(!isBlank(underCheck), "Attribute must not be blank"); 752 return underCheck; 753 } 754 755 /** 756 * Checks a string for being non-null and not empty (checks with trimming) Throws an 757 * {@link IllegalArgumentException} if String is null or empty 758 * 759 * @param underCheck 760 * @param attributeName used for the creation of the error text 761 * @return the given String 762 */ 763 public static String requireNotEmptyTrimmed(final String underCheck, final String attributeName) { 764 checkArgument(!isBlank(underCheck), "Attribute with name \'" + attributeName + "\' must not be blank"); 765 return underCheck; 766 } 767 768 /** 769 * Returns the given string if it is non-null; the empty string otherwise. 770 * 771 * @author com.google.common.base.Strings 772 * @param string the string to test and possibly return 773 * @return {@code string} itself if it is non-null; {@code ""} if it is null 774 */ 775 public static String nullToEmpty(String string) { 776 if (null == string) { 777 return ""; 778 } 779 return string; 780 } 781 782 /** 783 * Returns the given string if it is nonempty; {@code null} otherwise. 784 * 785 * @author com.google.common.base.Strings 786 * @param string the string to test and possibly return 787 * @return {@code string} itself if it is nonempty; {@code null} if it is empty or null 788 */ 789 public static String emptyToNull(String string) { 790 if (null == string || string.isEmpty()) { 791 return null; 792 } 793 return string; 794 } 795 796 /** 797 * Null-safe trimming of a String value. 798 * 799 * @param string to be trimmed 800 * 801 * @return <code>null</code> if the string is <code>null</code> otherwise the trimmed string 802 */ 803 public static String trimOrNull(final String string) { 804 return null != string ? string.trim() : null; 805 } 806 807 /** 808 * Returns the given {@code template} string with each occurrence of {@code "%s"} replaced with 809 * the corresponding argument value from {@code args}; or, if the placeholder and argument 810 * counts 811 * do not match, returns a best-effort form of that string. Will not throw an exception under 812 * normal conditions. 813 * 814 * <p> 815 * <b>Note:</b> For most string-formatting needs, use {@link String#format String.format}, 816 * {@link java.io.PrintWriter#format PrintWriter.format}, and related methods. These support the 817 * full range of <a 818 * href="https://docs.oracle.com/javase/9/docs/api/java/util/Formatter.html#syntax">format 819 * specifiers</a>, and alert you to usage errors by throwing {@link 820 * java.util.IllegalFormatException}. 821 * 822 * <p> 823 * In certain cases, such as outputting debugging information or constructing a message to be 824 * used for another unchecked exception, an exception during string formatting would serve 825 * little purpose except to supplant the real information you were trying to provide. These are 826 * the cases this method is made for; it instead generates a best-effort string with all 827 * supplied argument values present. This method is also useful in environments such as GWT 828 * where {@code String.format} is not available. 829 * 830 * <p> 831 * <b>Warning:</b> Only the exact two-character placeholder sequence {@code "%s"} is 832 * recognized. 833 * 834 * @author com.google.common.base.Strings 835 * @param template a string containing zero or more {@code "%s"} placeholder sequences. {@code 836 * null} is treated as the four-character string {@code "null"}. 837 * @param args the arguments to be substituted into the message template. The first argument 838 * specified is substituted for the first occurrence of {@code "%s"} in the template, 839 * and so forth. A {@code null} argument is converted to the four-character string 840 * {@code "null"}; non-null values are converted to strings using 841 * {@link Object#toString()}. 842 * @return the resulting formatting String 843 */ 844 public static String lenientFormat(String template, Object... args) { 845 template = String.valueOf(template); // null -> "null" 846 if (args == null) { 847 args = new Object[] {"(Object[])null"}; 848 } else { 849 for (var i = 0; i < args.length; i++) { 850 args[i] = lenientToString(args[i]); 851 } 852 } 853 // start substituting the arguments into the '%s' placeholders 854 var builder = new StringBuilder(template.length() + 16 * args.length); 855 var templateStart = 0; 856 var i = 0; 857 while (i < args.length) { 858 var placeholderStart = template.indexOf("%s", templateStart); 859 if (placeholderStart == -1) { 860 break; 861 } 862 builder.append(template, templateStart, placeholderStart); 863 builder.append(args[i]); 864 i++; 865 templateStart = placeholderStart + 2; 866 } 867 builder.append(template, templateStart, template.length()); 868 // if we run out of placeholders, append the extra args in square braces 869 if (i < args.length) { 870 builder.append(" ["); 871 builder.append(args[i]); 872 i++; 873 while (i < args.length) { 874 builder.append(", "); 875 builder.append(args[i]); 876 i++; 877 } 878 builder.append(']'); 879 } 880 return builder.toString(); 881 } 882 883 /** 884 * @param value to be processed 885 * @param suffix to be present 886 * @return value with suffix 887 */ 888 public static String ensureEndsWith(@NonNull final String value, @NonNull final String suffix) { 889 if (value == null) { 890 throw new java.lang.NullPointerException("value is marked non-null but is null"); 891 } 892 if (suffix == null) { 893 throw new java.lang.NullPointerException("suffix is marked non-null but is null"); 894 } 895 if (!value.endsWith(suffix)) { 896 return value + suffix; 897 } 898 return value; 899 } 900 901 /** 902 * @author com.google.common.base.Strings 903 */ 904 static String lenientToString(Object o) { 905 try { 906 return String.valueOf(o); 907 } catch (Exception e) { 908 // Default toString() behavior - see Object.toString() 909 var objectToString = o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o)); 910 log.warn(e, "Exception during lenientFormat for {}", objectToString); 911 return "<" + objectToString + " threw " + e.getClass().getName() + ">"; 912 } 913 } 914 915 /** 916 * @param checker the predicate to check each given value against. it decides if a value 917 * qualifies to be returned. 918 * @param values to be evaluated 919 * @return first string that is accepted by the given {@link Predicate} or 920 * {@link Optional#empty()} 921 */ 922 public static Optional<String> coalesce(Predicate<String> checker, String... values) { 923 if (null != values) { 924 for (String value : values) { 925 if (!checker.test(value)) { 926 return Optional.of(value); 927 } 928 } 929 } 930 return Optional.empty(); 931 } 932 933 /** 934 * @param values to be evaluated 935 * @return first string that is not {@link #isEmpty(CharSequence)} 936 */ 937 public static Optional<String> firstNonEmpty(String... values) { 938 return coalesce(MoreStrings::isEmpty, values); 939 } 940 941 /** 942 * @param values to be evaluated 943 * @return first string that is not {@link #isBlank(CharSequence)} 944 */ 945 public static Optional<String> firstNonBlank(String... values) { 946 return coalesce(MoreStrings::isBlank, values); 947 } 948 949 @java.lang.SuppressWarnings("all") 950 @lombok.Generated 951 private MoreStrings() { 952 throw new java.lang.UnsupportedOperationException("This is a utility class and cannot be instantiated"); 953 } 954}