001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.common.utils; 018 019import static java.util.stream.Collectors.*; 020import static org.apache.juneau.common.utils.StringUtils.*; 021 022import java.io.*; 023import java.lang.reflect.*; 024import java.nio.charset.*; 025import java.text.*; 026import java.time.format.*; 027import java.util.*; 028import java.util.concurrent.*; 029import java.util.function.*; 030import java.util.regex.*; 031import java.util.stream.*; 032 033/** 034 * Common utility methods. 035 * 036 * <p>This class contains various static utility methods for working with collections, strings, objects, and other common operations. 037 */ 038public class Utils { 039 040 private static final Map<Class<?>,Function<String,?>> ENV_FUNCTIONS = new IdentityHashMap<>(); 041 042 static { 043 ENV_FUNCTIONS.put(Boolean.class, Boolean::valueOf); 044 ENV_FUNCTIONS.put(Charset.class, Charset::forName); 045 } 046 047 private static final ConcurrentHashMap<String,String> PROPERTY_TO_ENV = new ConcurrentHashMap<>(); 048 049 /** 050 * Creates an array of objects. 051 * 052 * @param <T> The component type of the array. 053 * @param x The objects to place in the array. 054 * @return A new array containing the specified objects. 055 */ 056 @SafeVarargs 057 public static <T> T[] a(T...x) { 058 return x; 059 } 060 061 /** 062 * Traverses all elements in the specified object and accumulates them into a list. 063 * 064 * @param <T> The element type. 065 * @param o The object to traverse. 066 * @return A list containing all accumulated elements. 067 */ 068 public static <T> List<T> accumulate(Object o) { 069 var l = list(); 070 traverse(o, l::add); 071 return (List<T>) l; 072 } 073 074 /** 075 * Shortcut for creating an unmodifiable list out of an array of values. 076 * 077 * @param <T> The element type. 078 * @param values The values to add to the list. 079 * @return An unmodifiable list containing the specified values, or <jk>null</jk> if the input is <jk>null</jk>. 080 */ 081 @SafeVarargs 082 public static <T> List<T> alist(T...values) { // NOSONAR 083 return values == null ? null : Arrays.asList(values); 084 } 085 086 /** 087 * Converts the specified collection to an array. 088 * 089 * @param <E> The element type. 090 * @param value The collection to convert. 091 * @param componentType The component type of the array. 092 * @return A new array. 093 */ 094 public static <E> E[] array(Collection<E> value, Class<E> componentType) { 095 if (value == null) 096 return null; 097 E[] array = (E[])Array.newInstance(componentType, value.size()); 098 return value.toArray(array); 099 } 100 101 /** 102 * Converts any array (including primitive arrays) to a List. 103 * 104 * @param array The array to convert. Can be any array type including primitives. 105 * @return A List containing the array elements. Primitive values are auto-boxed. 106 * Returns null if the input is null. 107 * @throws IllegalArgumentException if the input is not an array. 108 */ 109 public static List<Object> arrayToList(Object array) { 110 if (array == null) { 111 return null; // NOSONAR 112 } 113 114 assertArg(isArray(array), "Input must be an array but was {0}", array.getClass().getName()); 115 116 var componentType = array.getClass().getComponentType(); 117 var length = Array.getLength(array); 118 var result = new ArrayList<>(length); 119 120 // Handle primitive arrays specifically for better performance 121 if (componentType.isPrimitive()) { 122 if (componentType == int.class) { 123 var arr = (int[]) array; 124 for (int value : arr) { 125 result.add(value); 126 } 127 } else if (componentType == long.class) { 128 var arr = (long[]) array; 129 for (long value : arr) { 130 result.add(value); 131 } 132 } else if (componentType == double.class) { 133 var arr = (double[]) array; 134 for (double value : arr) { 135 result.add(value); 136 } 137 } else if (componentType == float.class) { 138 var arr = (float[]) array; 139 for (float value : arr) { 140 result.add(value); 141 } 142 } else if (componentType == boolean.class) { 143 var arr = (boolean[]) array; 144 for (boolean value : arr) { 145 result.add(value); 146 } 147 } else if (componentType == byte.class) { 148 var arr = (byte[]) array; 149 for (byte value : arr) { 150 result.add(value); 151 } 152 } else if (componentType == char.class) { 153 var arr = (char[]) array; 154 for (char value : arr) { 155 result.add(value); 156 } 157 } else if (componentType == short.class) { 158 var arr = (short[]) array; 159 for (short value : arr) { 160 result.add(value); 161 } 162 } 163 } else { 164 // Handle Object arrays 165 for (var i = 0; i < length; i++) { 166 result.add(Array.get(array, i)); 167 } 168 } 169 170 return result; 171 } 172 173 /** 174 * Throws an {@link IllegalArgumentException} if the specified expression is <jk>false</jk>. 175 * 176 * <h5 class='section'>Example:</h5> 177 * <p class='bjava'> 178 * <jk>import static</jk> org.apache.juneau.internal.ArgUtils.*; 179 * 180 * <jk>public</jk> String setFoo(List<String> <jv>foo</jv>) { 181 * <jsm>assertArg</jsm>(<jv>foo</jv> != <jk>null</jk> && ! <jv>foo</jv>.isEmpty(), <js>"'foo' cannot be null or empty."</js>); 182 * ... 183 * } 184 * </p> 185 * 186 * @param expression The boolean expression to check. 187 * @param msg The exception message. 188 * @param args The exception message args. 189 * @throws IllegalArgumentException Constructed exception. 190 */ 191 public static final void assertArg(boolean expression, String msg, Object...args) throws IllegalArgumentException { 192 if (! expression) 193 throw new IllegalArgumentException(MessageFormat.format(msg, args)); 194 } 195 196 /** 197 * Throws an {@link IllegalArgumentException} if the specified argument is <jk>null</jk>. 198 * 199 * <h5 class='section'>Example:</h5> 200 * <p class='bjava'> 201 * <jk>import static</jk> org.apache.juneau.internal.ArgUtils.*; 202 * 203 * <jk>public</jk> String setFoo(String <jv>foo</jv>) { 204 * <jsm>assertArgNotNull</jsm>(<js>"foo"</js>, <jv>foo</jv>); 205 * ... 206 * } 207 * </p> 208 * 209 * @param <T> The argument data type. 210 * @param name The argument name. 211 * @param o The object to check. 212 * @return The same argument. 213 * @throws IllegalArgumentException Constructed exception. 214 */ 215 public static final <T> T assertArgNotNull(String name, T o) throws IllegalArgumentException { 216 assertArg(o != null, "Argument ''{0}'' cannot be null.", name); 217 return o; 218 } 219 220 /** 221 * Throws an {@link IllegalArgumentException} if the specified string is <jk>null</jk> or blank. 222 * 223 * @param name The argument name. 224 * @param o The object to check. 225 * @return The same object. 226 * @throws IllegalArgumentException Thrown if the specified string is <jk>null</jk> or blank. 227 */ 228 public static final String assertArgNotNullOrBlank(String name, String o) throws IllegalArgumentException { 229 assertArg(o != null, "Argument ''{0}'' cannot be null.", name); 230 assertArg(! o.isBlank(), "Argument ''{0}'' cannot be blank.", name); 231 return o; 232 } 233 234 /** 235 * Throws an {@link IllegalArgumentException} if the specified varargs array or any of its elements are <jk>null</jk>. 236 * 237 * @param <T> The element type. 238 * @param name The argument name. 239 * @param o The object to check. 240 * @return The same object. 241 * @throws IllegalArgumentException Thrown if the specified varargs array or any of its elements are <jk>null</jk>. 242 */ 243 public static final <T> T[] assertVarargsNotNull(String name, T[] o) throws IllegalArgumentException { 244 assertArg(o != null, "Argument ''{0}'' cannot be null.", name); 245 for (int i = 0; i < o.length; i++) 246 assertArg(o[i] != null, "Argument ''{0}'' parameter {1} cannot be null.", name, i); 247 return o; 248 } 249 250 /** 251 * Throws an {@link IllegalArgumentException} if the specified value doesn't have all subclasses of the specified type. 252 * 253 * @param <E> The element type. 254 * @param name The argument name. 255 * @param type The expected parent class. 256 * @param value The array value being checked. 257 * @return The value cast to the specified array type. 258 * @throws IllegalArgumentException Constructed exception. 259 */ 260 public static final <E> Class<E>[] assertClassArrayArgIsType(String name, Class<E> type, Class<?>[] value) throws IllegalArgumentException { 261 for (var i = 0; i < value.length; i++) 262 if (! type.isAssignableFrom(value[i])) 263 throw new IllegalArgumentException("Arg "+name+" did not have arg of type "+type.getName()+" at index "+i+": "+value[i].getName()); 264 return (Class<E>[])value; 265 } 266 267 /** 268 * Casts an object to a specific type if it's an instance of that type. 269 * 270 * @param <T> The type to cast to. 271 * @param c The type to cast to. 272 * @param o The object to cast to. 273 * @return The cast object, or <jk>null</jk> if the object wasn't the specified type. 274 */ 275 public static <T> T cast(Class<T> c, Object o) { 276 return o != null && c.isInstance(o) ? c.cast(o) : null; 277 } 278 279 /** 280 * If the specified object is an instance of the specified class, casts it to that type. 281 * 282 * @param <T> The class to cast to. 283 * @param o The object to cast. 284 * @param c The class to cast to. 285 * @return The cast object, or <jk>null</jk> if the object wasn't an instance of the specified class. 286 */ 287 public static <T> T castOrNull(Object o, Class<T> c) { 288 if (c.isInstance(o)) 289 return c.cast(o); 290 return null; 291 } 292 293 /** 294 * Compares two objects for equality. 295 * 296 * <p> 297 * Nulls are always considered less-than unless both are null. 298 * 299 * @param o1 Object 1. 300 * @param o2 Object 2. 301 * @return 302 * <c>-1</c>, <c>0</c>, or <c>1</c> if <c>o1</c> is less-than, equal, or greater-than <c>o2</c>. 303 * <br><c>0</c> if objects are not of the same type or do not implement the {@link Comparable} interface. 304 */ 305 @SuppressWarnings({ "rawtypes", "unchecked" }) 306 public static int compare(Object o1, Object o2) { 307 if (o1 == null) { 308 if (o2 == null) 309 return 0; 310 return -1; 311 } else if (o2 == null) { 312 return 1; 313 } 314 315 if (o1.getClass() == o2.getClass() && o1 instanceof Comparable) 316 return ((Comparable)o1).compareTo(o2); 317 318 return 0; 319 } 320 321 /** 322 * Null-safe {@link String#contains(CharSequence)} operation. 323 * 324 * @param s The string to check. 325 * @param values The characters to check for. 326 * @return <jk>true</jk> if the string contains any of the specified characters. 327 */ 328 public static boolean contains(String s, char...values) { 329 if (s == null || values == null || values.length == 0) 330 return false; 331 for (var v : values) { 332 if (s.indexOf(v) >= 0) 333 return true; 334 } 335 return false; 336 } 337 338 /** 339 * Null-safe {@link String#contains(CharSequence)} operation. 340 * 341 * @param s The string to check. 342 * @param values The substrings to check for. 343 * @return <jk>true</jk> if the string contains any of the specified substrings. 344 */ 345 public static boolean contains(String s, String...values) { 346 if (s == null || values == null || values.length == 0) 347 return false; 348 for (var v : values) { 349 if (s.contains(v)) 350 return true; 351 } 352 return false; 353 } 354 355 /** 356 * Creates an empty array of the specified type. 357 * 358 * @param <T> The component type of the array. 359 * @param type The component type class. 360 * @return An empty array of the specified type. 361 */ 362 public static <T> T[] ea(Class<T> type) { 363 return (T[])Array.newInstance(type, 0); 364 } 365 366 /** 367 * Shortcut for creating an empty list of the specified type. 368 * 369 * @param <T> The element type. 370 * @param type The element type class. 371 * @return An empty list. 372 */ 373 public static <T> List<T> elist(Class<T> type) { 374 return Collections.emptyList(); 375 } 376 377 /** 378 * Shortcut for creating an empty map of the specified types. 379 * 380 * @param <K> The key type. 381 * @param <V> The value type. 382 * @param keyType The key type class. 383 * @param valueType The value type class. 384 * @return An empty unmodifiable map. 385 */ 386 public static <K,V> Map<K,V> emap(Class<K> keyType, Class<V> valueType) { 387 return Collections.emptyMap(); 388 } 389 390 /** 391 * Returns an empty {@link Optional}. 392 * 393 * @param <T> The component type. 394 * @return An empty {@link Optional}. 395 */ 396 public static <T> Optional<T> empty() { 397 return Optional.empty(); 398 } 399 400 /** 401 * Returns the specified string, or blank if that string is null. 402 * 403 * @param value The value to convert to a string. 404 * @return The string representation of the value, or an empty string if <jk>null</jk>. 405 */ 406 public static String emptyIfNull(Object value) { 407 return value == null ? "" : value.toString(); 408 } 409 410 /** 411 * Looks up a system property or environment variable. 412 * 413 * <p> 414 * First looks in system properties. Then converts the name to env-safe and looks in the system environment. 415 * Then returns the default if it can't be found. 416 * 417 * @param name The property name. 418 * @return The value if found. 419 */ 420 public static Optional<String> env(String name) { 421 var s = System.getProperty(name); 422 if (s == null) 423 s = System.getenv(envName(name)); 424 return opt(s); 425 } 426 427 /** 428 * Looks up a system property or environment variable. 429 * 430 * <p> 431 * First looks in system properties. Then converts the name to env-safe and looks in the system environment. 432 * Then returns the default if it can't be found. 433 * 434 * @param <T> The type to convert the value to. 435 * @param name The property name. 436 * @param def The default value if not found. 437 * @return The default value. 438 */ 439 public static <T> T env(String name, T def) { 440 return env(name).map(x -> toType(x, def)).orElse(def); 441 } 442 443 /** 444 * Converts a property name to an environment variable name. 445 * 446 * @param name The property name to convert. 447 * @return The environment variable name (uppercase with dots replaced by underscores). 448 */ 449 private static String envName(String name) { 450 return PROPERTY_TO_ENV.computeIfAbsent(name, x->x.toUpperCase().replace(".", "_")); 451 } 452 453 /** 454 * Tests two strings for equality, but gracefully handles nulls. 455 * 456 * @param caseInsensitive Use case-insensitive matching. 457 * @param s1 String 1. 458 * @param s2 String 2. 459 * @return <jk>true</jk> if the strings are equal. 460 */ 461 public static boolean eq(boolean caseInsensitive, String s1, String s2) { 462 return caseInsensitive ? eqic(s1, s2) : eq(s1, s2); 463 } 464 465 /** 466 * Tests two objects for equality, gracefully handling nulls and arrays. 467 * 468 * @param <T> The value types. 469 * @param o1 Object 1. 470 * @param o2 Object 2. 471 * @return <jk>true</jk> if both objects are equal based on the {@link Object#equals(Object)} method. 472 */ 473 public static <T> boolean eq(T o1, T o2) { 474 if (isArray(o1) && isArray(o2)) { 475 int l1 = Array.getLength(o1), l2 = Array.getLength(o2); 476 if (l1 != l2) 477 return false; 478 for (int i = 0; i < l1; i++) 479 if (! eq(Array.get(o1, i), Array.get(o2, i))) 480 return false; 481 return true; 482 } 483 return Objects.equals(o1, o2); 484 } 485 486 /** 487 * Tests two objects for equality, gracefully handling nulls. 488 * 489 * Allows you to simplify object comparison without sacrificing efficiency. 490 * 491 * Example: 492 * <code> 493 * public boolean equals(Object o) 494 * return eq(this, (Role)o, (x,y)->eq(x.id,y.id) && eq(x.name,y.name) && eq(x.created,y.created) && eq(x.createdBy,y.createdBy)); 495 * } 496 * </code> 497 * 498 * @param <T> Object 1 type. 499 * @param <U> Object 2 type. 500 * @param o1 Object 1. 501 * @param o2 Object 2. 502 * @param test The test to use for equality. 503 * @return <jk>true</jk> if both objects are equal based on the test. 504 */ 505 public static <T,U> boolean eq(T o1, U o2, BiPredicate<T,U> test) { 506 if (o1 == null) { return o2 == null; } 507 if (o2 == null) { return false; } 508 if (o1 == o2) { return true; } 509 return test.test(o1, o2); 510 } 511 512 /** 513 * Tests two strings for case-insensitive equality, but gracefully handles nulls. 514 * 515 * @param s1 String 1. 516 * @param s2 String 2. 517 * @return <jk>true</jk> if the strings are equal. 518 */ 519 public static boolean eqic(String s1, String s2) { 520 if (s1 == null) 521 return s2 == null; 522 if (s2 == null) 523 return false; 524 return s1.equalsIgnoreCase(s2); 525 } 526 527 /** 528 * Same as MessageFormat.format(). 529 * 530 * @param pattern The message pattern. 531 * @param args The arguments to substitute into the pattern. 532 * @return The formatted string. 533 */ 534 public static String f(String pattern, Object...args) { 535 if (args.length == 0) 536 return pattern; 537 return MessageFormat.format(pattern, args); 538 } 539 540 /** 541 * Returns the first non-null value in the specified array 542 * 543 * @param <T> The value types. 544 * @param t The values to check. 545 * @return The first non-null value, or <jk>null</jk> if the array is null or empty or contains only <jk>null</jk> values. 546 */ 547 @SafeVarargs 548 public static <T> T firstNonNull(T... t) { 549 if (t != null) 550 for (T tt : t) 551 if (tt != null) 552 return tt; 553 return null; 554 } 555 556 /** 557 * Creates a formatted string supplier with message arguments for lazy evaluation. 558 * 559 * <p>This method returns a {@link Supplier} that formats the string pattern with the provided arguments 560 * only when the supplier's {@code get()} method is called. This is useful for expensive string formatting 561 * operations that may not always be needed, such as error messages in assertions.</p> 562 * 563 * <h5 class='section'>Usage Examples:</h5> 564 * <p class='bjava'> 565 * <jc>// Lazy evaluation - string is only formatted if assertion fails</jc> 566 * assertTrue(condition, fms(<js>"Expected {0} but got {1}"</js>, expected, actual)); 567 * 568 * <jc>// Can be used anywhere a Supplier<String> is expected</jc> 569 * Supplier<String> <jv>messageSupplier</jv> = fms(<js>"Processing item {0} of {1}"</js>, i, total); 570 * </p> 571 * 572 * @param pattern The message pattern using <js>{0}</js>, <js>{1}</js>, etc. placeholders. 573 * @param args The arguments to substitute into the pattern placeholders. 574 * @return A {@link Supplier} that will format the string when {@code get()} is called. 575 * @see StringUtils#format(String, Object...) 576 */ 577 public static Supplier<String> fms(String pattern, Object...args) { 578 return ()->StringUtils.format(pattern, args); 579 } 580 581 /** 582 * Converts a string containing <js>"*"</js> meta characters with a regular expression pattern. 583 * 584 * @param s The string to create a pattern from. 585 * @return A regular expression pattern. 586 */ 587 public static Pattern getMatchPattern3(String s) { 588 return getMatchPattern3(s, 0); 589 } 590 591 /** 592 * Converts a string containing <js>"*"</js> meta characters with a regular expression pattern. 593 * 594 * @param s The string to create a pattern from. 595 * @param flags Regular expression flags. 596 * @return A regular expression pattern. 597 */ 598 public static Pattern getMatchPattern3(String s, int flags) { 599 if (s == null) 600 return null; 601 var sb = new StringBuilder(); 602 sb.append("\\Q"); 603 for (var i = 0; i < s.length(); i++) { 604 var c = s.charAt(i); 605 if (c == '*') 606 sb.append("\\E").append(".*").append("\\Q"); 607 else if (c == '?') 608 sb.append("\\E").append(".").append("\\Q"); 609 else 610 sb.append(c); 611 } 612 sb.append("\\E"); 613 return Pattern.compile(sb.toString(), flags); 614 } 615 616 /** 617 * Shortcut for calling {@link Objects#hash(Object...)}. 618 * 619 * @param values The values to hash. 620 * @return A hash code value for the given values. 621 */ 622 public static final int hash(Object...values) { 623 return Objects.hash(values); 624 } 625 626 /** 627 * Creates an {@link IllegalArgumentException}. 628 * 629 * @param msg The exception message. 630 * @param args The arguments to substitute into the message. 631 * @return A new IllegalArgumentException with the formatted message. 632 */ 633 public static IllegalArgumentException illegalArg(String msg, Object...args) { 634 return new IllegalArgumentException(args.length == 0 ? msg : f(msg, args)); 635 } 636 637 /** 638 * Checks if the specified object is an array. 639 * 640 * @param o The object to check. 641 * @return <jk>true</jk> if the object is not <jk>null</jk> and is an array. 642 */ 643 public static boolean isArray(Object o) { 644 return o != null && o.getClass().isArray(); 645 } 646 647 /** 648 * Returns <jk>true</jk> if the specified object is empty. 649 * 650 * <p> 651 * Return <jk>true</jk> if the value is any of the following: 652 * <ul> 653 * <li><jk>null</jk> 654 * <li>An empty Collection 655 * <li>An empty Map 656 * <li>An empty array 657 * <li>An empty CharSequence 658 * <li>An empty String when serialized to a string using {@link Object#toString()}. 659 * </ul> 660 * 661 * @param o The object to test. 662 * @return <jk>true</jk> if the specified object is empty. 663 */ 664 public static boolean isEmpty(Object o) { 665 if (o == null) 666 return true; 667 if (o instanceof Collection<?> o2) 668 return o2.isEmpty(); 669 if (o instanceof Map<?,?> o2) 670 return o2.isEmpty(); 671 if (isArray(o)) 672 return (Array.getLength(o) == 0); 673 return o.toString().isEmpty(); 674 } 675 676 /** 677 * Returns <jk>true</jk> if string is <jk>null</jk> or empty. 678 * 679 * @param o The string to check. 680 * @return <jk>true</jk> if string is <jk>null</jk> or empty. 681 */ 682 public static boolean isEmpty(String o) { 683 return o == null || o.isEmpty(); 684 } 685 686 /** 687 * Returns <jk>true</jk> if specified string is <jk>null</jk> or empty or consists of only blanks. 688 * 689 * @param s The string to check. 690 * @return <jk>true</jk> if specified string is <jk>null</jk> or empty or consists of only blanks. 691 */ 692 public static boolean isEmptyOrBlank(String s) { 693 return s == null || s.trim().isEmpty(); 694 } 695 696 /** 697 * Returns <jk>true</jk> if the specified object is not <jk>null</jk> and not empty. 698 * 699 * Works on any of the following data types: String, CharSequence, Collection, Map, array. 700 * All other types are stringified and then checked as a String. 701 * 702 * @param value The value being checked. 703 * @return <jk>true</jk> if the specified object is not <jk>null</jk> and not empty. 704 */ 705 public static boolean isNotEmpty(Object value) { 706 if (value == null) return false; 707 if (value instanceof CharSequence x) return ! x.isEmpty(); 708 if (value instanceof Collection<?> x) return ! x.isEmpty(); 709 if (value instanceof Map<?,?> x) return ! x.isEmpty(); 710 if (isArray(value)) return Array.getLength(value) > 0; 711 return isNotEmpty(s(value)); 712 } 713 714 /** 715 * Returns <jk>true</jk> if string is not <jk>null</jk> and not empty. 716 * 717 * @param o The string to check. 718 * @return <jk>true</jk> if string is not <jk>null</jk> and not empty. 719 */ 720 public static boolean isNotEmpty(String o) { 721 return ! isEmpty(o); 722 } 723 724 /** 725 * Returns <jk>true</jk> if the specified number is not <jk>null</jk> and not <c>-1</c>. 726 * 727 * @param <T> The value types. 728 * @param value The value being checked. 729 * @return <jk>true</jk> if the specified number is not <jk>null</jk> and not <c>-1</c>. 730 */ 731 public static <T extends Number> boolean isNotMinusOne(T value) { 732 return value != null && value.intValue() != -1; 733 } 734 735 /** 736 * Returns <jk>true</jk> if the specified object is not <jk>null</jk>. 737 * 738 * @param <T> The value type. 739 * @param value The value being checked. 740 * @return <jk>true</jk> if the specified object is not <jk>null</jk>. 741 */ 742 public static <T> boolean isNotNull(T value) { 743 return value != null; 744 } 745 746 /** 747 * Returns <jk>true</jk> if the specified boolean is not <jk>null</jk> and is <jk>true</jk>. 748 * 749 * @param value The value being checked. 750 * @return <jk>true</jk> if the specified boolean is not <jk>null</jk> and is <jk>true</jk>. 751 */ 752 public static boolean isTrue(Boolean value) { 753 return value != null && value; 754 } 755 756 /** 757 * Join the specified tokens into a delimited string. 758 * 759 * @param tokens The tokens to join. 760 * @param d The delimiter. 761 * @return The delimited string. If <c>tokens</c> is <jk>null</jk>, returns <jk>null</jk>. 762 */ 763 public static String join(Collection<?> tokens, char d) { 764 if (tokens == null) 765 return null; 766 var sb = new StringBuilder(); 767 for (var iter = tokens.iterator(); iter.hasNext();) { 768 sb.append(iter.next()); 769 if (iter.hasNext()) 770 sb.append(d); 771 } 772 return sb.toString(); 773 } 774 775 /** 776 * Join the specified tokens into a delimited string. 777 * 778 * @param tokens The tokens to join. 779 * @param d The delimiter. 780 * @return The delimited string. If <c>tokens</c> is <jk>null</jk>, returns <jk>null</jk>. 781 */ 782 public static String join(Collection<?> tokens, String d) { 783 if (tokens == null) 784 return null; 785 return join(tokens, d, new StringBuilder()).toString(); 786 } 787 788 /** 789 * Joins the specified tokens into a delimited string and writes the output to the specified string builder. 790 * 791 * @param tokens The tokens to join. 792 * @param d The delimiter. 793 * @param sb The string builder to append the response to. 794 * @return The same string builder passed in as <c>sb</c>. 795 */ 796 public static StringBuilder join(Collection<?> tokens, String d, StringBuilder sb) { 797 if (tokens == null) 798 return sb; 799 for (var iter = tokens.iterator(); iter.hasNext();) { 800 sb.append(iter.next()); 801 if (iter.hasNext()) 802 sb.append(d); 803 } 804 return sb; 805 } 806 807 /** 808 * Join the specified tokens into a delimited string. 809 * 810 * @param tokens The tokens to join. 811 * @param d The delimiter. 812 * @return The delimited string. If <c>tokens</c> is <jk>null</jk>, returns <jk>null</jk>. 813 */ 814 public static String join(int[] tokens, char d) { 815 if (tokens == null) 816 return null; 817 var sb = new StringBuilder(); 818 for (var i = 0; i < tokens.length; i++) { 819 if (i > 0) 820 sb.append(d); 821 sb.append(tokens[i]); 822 } 823 return sb.toString(); 824 } 825 826 /** 827 * Join the specified tokens into a delimited string. 828 * 829 * @param tokens The tokens to join. 830 * @param d The delimiter. 831 * @return The delimited string. If <c>tokens</c> is <jk>null</jk>, returns <jk>null</jk>. 832 */ 833 public static String join(List<?> tokens, char d) { 834 if (tokens == null) 835 return null; 836 var sb = new StringBuilder(); 837 for (int i = 0, j = tokens.size(); i < j; i++) { 838 if (i > 0) 839 sb.append(d); 840 sb.append(tokens.get(i)); 841 } 842 return sb.toString(); 843 } 844 845 /** 846 * Join the specified tokens into a delimited string. 847 * 848 * @param tokens The tokens to join. 849 * @param d The delimiter. 850 * @return The delimited string. If <c>tokens</c> is <jk>null</jk>, returns <jk>null</jk>. 851 */ 852 public static String join(List<?> tokens, String d) { 853 if (tokens == null) 854 return null; 855 return join(tokens, d, new StringBuilder()).toString(); 856 } 857 858 /** 859 * Joins the specified tokens into a delimited string and writes the output to the specified string builder. 860 * 861 * @param tokens The tokens to join. 862 * @param d The delimiter. 863 * @param sb The string builder to append the response to. 864 * @return The same string builder passed in as <c>sb</c>. 865 */ 866 public static StringBuilder join(List<?> tokens, String d, StringBuilder sb) { 867 if (tokens == null) 868 return sb; 869 for (int i = 0, j = tokens.size(); i < j; i++) { 870 if (i > 0) 871 sb.append(d); 872 sb.append(tokens.get(i)); 873 } 874 return sb; 875 } 876 877 /** 878 * Joins the specified tokens into a delimited string. 879 * 880 * @param tokens The tokens to join. 881 * @param d The delimiter. 882 * @return The delimited string. If <c>tokens</c> is <jk>null</jk>, returns <jk>null</jk>. 883 */ 884 public static String join(Object[] tokens, char d) { 885 if (tokens == null) 886 return null; 887 if (tokens.length == 1) 888 return emptyIfNull(s(tokens[0])); 889 return join(tokens, d, new StringBuilder()).toString(); 890 } 891 892 /** 893 * Join the specified tokens into a delimited string and writes the output to the specified string builder. 894 * 895 * @param tokens The tokens to join. 896 * @param d The delimiter. 897 * @param sb The string builder to append the response to. 898 * @return The same string builder passed in as <c>sb</c>. 899 */ 900 public static StringBuilder join(Object[] tokens, char d, StringBuilder sb) { 901 if (tokens == null) 902 return sb; 903 for (var i = 0; i < tokens.length; i++) { 904 if (i > 0) 905 sb.append(d); 906 sb.append(tokens[i]); 907 } 908 return sb; 909 } 910 911 /** 912 * Join the specified tokens into a delimited string. 913 * 914 * @param tokens The tokens to join. 915 * @param separator The delimiter. 916 * @return The delimited string. If <c>tokens</c> is <jk>null</jk>, returns <jk>null</jk>. 917 */ 918 public static String join(Object[] tokens, String separator) { 919 if (tokens == null) 920 return null; 921 var sb = new StringBuilder(); 922 for (var i = 0; i < tokens.length; i++) { 923 if (i > 0) 924 sb.append(separator); 925 sb.append(tokens[i]); 926 } 927 return sb.toString(); 928 } 929 930 /** 931 * Joins tokens with newlines. 932 * 933 * @param tokens The tokens to concatenate. 934 * @return A string with the specified tokens contatenated with newlines. 935 */ 936 public static String joinnl(Object[] tokens) { 937 return join(tokens, '\n'); 938 } 939 940 /** 941 * Shortcut for creating a modifiable list out of an array of values. 942 * 943 * @param <T> The element type. 944 * @param values The values to add to the list. 945 * @return A modifiable list containing the specified values. 946 */ 947 @SafeVarargs 948 public static <T> List<T> list(T...values) { // NOSONAR 949 return new ArrayList<>(Arrays.asList(values)); 950 } 951 952 /** 953 * Convenience method for creating an {@link ArrayList} of the specified size. 954 * 955 * @param <E> The element type. 956 * @param size The initial size of the list. 957 * @return A new modifiable list. 958 */ 959 public static <E> ArrayList<E> listOfSize(int size) { 960 return new ArrayList<>(size); 961 } 962 963 /** 964 * Shortcut for creating a modifiable map out of an array of key-value pairs. 965 * 966 * @param <K> The key type. 967 * @param <V> The value type. 968 * @param values The key-value pairs (alternating keys and values). 969 * @return A modifiable LinkedHashMap containing the specified key-value pairs. 970 */ 971 @SafeVarargs 972 public static <K,V> LinkedHashMap<K,V> map(Object...values) { // NOSONAR 973 var m = new LinkedHashMap<K,V>(); 974 for (var i = 0; i < values.length; i+=2) { 975 m.put((K)values[i], (V)values[i+1]); 976 } 977 return m; 978 } 979 980 /** 981 * Returns <jk>null</jk> for the specified type. 982 * 983 * @param <T> The type. 984 * @param type The type class. 985 * @return <jk>null</jk>. 986 */ 987 public static <T> T n(Class<T> type) { 988 return null; 989 } 990 991 /** 992 * Returns <jk>null</jk> for the specified array type. 993 * 994 * @param <T> The component type. 995 * @param type The component type class. 996 * @return <jk>null</jk>. 997 */ 998 public static <T> T[] na(Class<T> type) { 999 return null; 1000 } 1001 1002 /** 1003 * Null-safe not-equals check. 1004 * 1005 * @param <T> The object type. 1006 * @param s1 Object 1. 1007 * @param s2 Object 2. 1008 * @return <jk>true</jk> if the objects are not equal. 1009 */ 1010 public static <T> boolean ne(T s1, T s2) { 1011 return ! eq(s1, s2); 1012 } 1013 1014 /** 1015 * Tests two objects for inequality, gracefully handling nulls. 1016 * 1017 * @param <T> Object 1 type. 1018 * @param <U> Object 2 type. 1019 * @param o1 Object 1. 1020 * @param o2 Object 2. 1021 * @param test The test to use for equality. 1022 * @return <jk>false</jk> if both objects are equal based on the test. 1023 */ 1024 public static <T,U> boolean ne(T o1, U o2, BiPredicate<T,U> test) { 1025 if (o1 == null) 1026 return o2 != null; 1027 if (o2 == null) 1028 return true; 1029 if (o1 == o2) 1030 return false; 1031 return ! test.test(o1, o2); 1032 } 1033 1034 /** 1035 * Tests two strings for non-equality ignoring case, but gracefully handles nulls. 1036 * 1037 * @param s1 String 1. 1038 * @param s2 String 2. 1039 * @return <jk>true</jk> if the strings are not equal ignoring case. 1040 */ 1041 public static boolean neic(String s1, String s2) { 1042 return ! eqic(s1, s2); 1043 } 1044 1045 /** 1046 * Returns a null list. 1047 * 1048 * @param <T> The element type. 1049 * @param type The element type class. 1050 * @return <jk>null</jk>. 1051 */ 1052 public static <T> List<T> nlist(Class<T> type) { 1053 return null; 1054 } 1055 1056 /** 1057 * Returns a null map. 1058 * 1059 * @param <K> The key type. 1060 * @param <V> The value type. 1061 * @param keyType The key type class. 1062 * @param valueType The value type class. 1063 * @return <jk>null</jk>. 1064 */ 1065 public static <K,V> Map<K,V> nmap(Class<K> keyType, Class<V> valueType) { 1066 return null; 1067 } 1068 1069 /** 1070 * Null-safe string not-contains operation. 1071 * 1072 * @param s The string to check. 1073 * @param values The characters to check for. 1074 * @return <jk>true</jk> if the string does not contain any of the specified characters. 1075 */ 1076 public static boolean notContains(String s, char...values) { 1077 return ! contains(s, values); 1078 } 1079 1080 /** 1081 * Returns the specified string, or <jk>null</jk> if that string is <jk>null</jk> or empty. 1082 * 1083 * @param value The string value to check. 1084 * @return The string value, or <jk>null</jk> if the string is <jk>null</jk> or empty. 1085 */ 1086 public static String nullIfEmpty(String value) { 1087 return isEmpty(value) ? null : value; 1088 } 1089 1090 /** 1091 * Returns <jk>null</jk> if the specified string is <jk>null</jk> or empty. 1092 * 1093 * @param s The string to check. 1094 * @return <jk>null</jk> if the specified string is <jk>null</jk> or empty, or the same string if not. 1095 */ 1096 public static String nullIfEmpty3(String s) { 1097 if (s == null || s.isEmpty()) 1098 return null; 1099 return s; 1100 } 1101 1102 /** 1103 * Returns an obfuscated version of the specified string. 1104 * 1105 * @param s The string to obfuscate. 1106 * @return The obfuscated string with most characters replaced by asterisks. 1107 */ 1108 public static String obfuscate(String s) { 1109 if (s == null || s.length() < 2) 1110 return "*"; 1111 return s.substring(0, 1) + s.substring(1).replaceAll(".", "*"); // NOSONAR 1112 } 1113 1114 /** 1115 * Shortcut for calling {@link Optional#ofNullable(Object)}. 1116 * 1117 * @param <T> The object type. 1118 * @param t The object to wrap in an Optional. 1119 * @return An Optional containing the specified object, or empty if <jk>null</jk>. 1120 */ 1121 public static final <T> Optional<T> opt(T t) { 1122 return Optional.ofNullable(t); 1123 } 1124 1125 /** 1126 * Returns an empty Optional. 1127 * 1128 * @param <T> The object type. 1129 * @return An empty Optional. 1130 */ 1131 public static final <T> Optional<T> opte() { 1132 return Optional.empty(); 1133 } 1134 1135 /** 1136 * Prints all the specified lines to System.out. 1137 * 1138 * @param lines The lines to print. 1139 */ 1140 public static final void printLines(String[] lines) { 1141 for (var i = 0; i < lines.length; i++) 1142 System.out.println(String.format("%4s:" + lines[i], i+1)); // NOSONAR - NOT DEBUG 1143 } 1144 1145 /** 1146 * Converts an arbitrary object to a readable string format suitable for debugging and testing. 1147 * 1148 * <p>This method provides intelligent formatting for various Java types, recursively processing 1149 * nested structures to create human-readable representations. It's extensively used throughout 1150 * the Juneau framework for test assertions and debugging output.</p> 1151 * 1152 * <h5 class='section'>Type-Specific Formatting:</h5> 1153 * <ul> 1154 * <li><b>null:</b> Returns <js>null</js></li> 1155 * <li><b>Optional:</b> Recursively formats the contained value (or <js>null</js> if empty)</li> 1156 * <li><b>Collections:</b> Formats as <js>"[item1,item2,item3]"</js> with comma-separated elements</li> 1157 * <li><b>Maps:</b> Formats as <js>"{key1=value1,key2=value2}"</js> with comma-separated entries</li> 1158 * <li><b>Map.Entry:</b> Formats as <js>"key=value"</js></li> 1159 * <li><b>Arrays:</b> Converts to list format <js>"[item1,item2,item3]"</js></li> 1160 * <li><b>Iterables/Iterators/Enumerations:</b> Converts to list and formats recursively</li> 1161 * <li><b>GregorianCalendar:</b> Formats as ISO instant timestamp</li> 1162 * <li><b>Date:</b> Formats as ISO instant string (e.g., <js>"2023-12-25T10:30:00Z"</js>)</li> 1163 * <li><b>InputStream:</b> Converts to hexadecimal representation</li> 1164 * <li><b>Reader:</b> Reads content and returns as string</li> 1165 * <li><b>File:</b> Reads file content and returns as string</li> 1166 * <li><b>byte[]:</b> Converts to hexadecimal representation</li> 1167 * <li><b>Enum:</b> Returns the enum name via {@link Enum#name()}</li> 1168 * <li><b>All other types:</b> Uses {@link Object#toString()}</li> 1169 * </ul> 1170 * 1171 * <h5 class='section'>Examples:</h5> 1172 * <p class='bjava'> 1173 * <jc>// Collections</jc> 1174 * r(List.of("a", "b", "c")) <jc>// Returns: "[a,b,c]"</jc> 1175 * r(Set.of(1, 2, 3)) <jc>// Returns: "[1,2,3]" (order may vary)</jc> 1176 * 1177 * <jc>// Maps</jc> 1178 * r(Map.of("foo", "bar", "baz", 123)) <jc>// Returns: "{foo=bar,baz=123}"</jc> 1179 * 1180 * <jc>// Arrays</jc> 1181 * r(new int[]{1, 2, 3}) <jc>// Returns: "[1,2,3]"</jc> 1182 * r(new String[]{"a", "b"}) <jc>// Returns: "[a,b]"</jc> 1183 * 1184 * <jc>// Nested structures</jc> 1185 * r(List.of(Map.of("x", 1), Set.of("a", "b"))) <jc>// Returns: "[{x=1},[a,b]]"</jc> 1186 * 1187 * <jc>// Special types</jc> 1188 * r(Optional.of("test")) <jc>// Returns: "test"</jc> 1189 * r(Optional.empty()) <jc>// Returns: null</jc> 1190 * r(new Date(1640995200000L)) <jc>// Returns: "2022-01-01T00:00:00Z"</jc> 1191 * r(MyEnum.FOO) <jc>// Returns: "FOO"</jc> 1192 * </p> 1193 * 1194 * <h5 class='section'>Recursive Processing:</h5> 1195 * <p>The method recursively processes nested structures, so complex objects containing 1196 * collections, maps, and arrays are fully flattened into readable format. This makes it 1197 * ideal for test assertions where you need to compare complex object structures.</p> 1198 * 1199 * <h5 class='section'>Error Handling:</h5> 1200 * <p>IO operations (reading files, streams) are wrapped in safe() calls, converting 1201 * any exceptions to RuntimeExceptions. Binary data (InputStreams, byte arrays) is 1202 * converted to hexadecimal representation for readability.</p> 1203 * 1204 * @param o The object to convert to readable format. Can be <jk>null</jk>. 1205 * @return A readable string representation of the object, or <jk>null</jk> if the input was <jk>null</jk>. 1206 * @see #safe(ThrowingSupplier) 1207 */ 1208 public static String r(Object o) { 1209 if (o == null) 1210 return null; 1211 if (o instanceof Optional<?> o2) 1212 return r(o2.orElse(null)); 1213 if (o instanceof Collection<?> o2) 1214 return o2.stream().map(Utils::r).collect(joining(",","[","]")); 1215 if (o instanceof Map<?,?> o2) 1216 return o2.entrySet().stream().map(Utils::r).collect(joining(",","{","}")); 1217 if (o instanceof Map.Entry<?,?> o2) 1218 return r(o2.getKey()) + '=' + r(o2.getValue()); 1219 if (o instanceof Iterable<?> o2) 1220 return r(toList(o2)); 1221 if (o instanceof Iterator<?> o2) 1222 return r(toList(o2)); 1223 if (o instanceof Enumeration<?> o2) 1224 return r(toList(o2)); 1225 if (o instanceof GregorianCalendar o2) 1226 return o2.toZonedDateTime().format(DateTimeFormatter.ISO_INSTANT); 1227 if (o instanceof Date o2) 1228 return o2.toInstant().toString(); 1229 if (o instanceof InputStream o2) 1230 return toHex(o2); 1231 if (o instanceof Reader o2) 1232 return safe(()->IOUtils.read(o2)); 1233 if (o instanceof File o2) 1234 return safe(()->IOUtils.read(o2)); 1235 if (o instanceof byte[] o2) 1236 return toHex(o2); 1237 if (o instanceof Enum o2) 1238 return o2.name(); 1239 if (o instanceof Class o2) 1240 return o2.getSimpleName(); 1241 if (o instanceof Executable o2) { 1242 var sb = new StringBuilder(64); 1243 sb.append(o2 instanceof Constructor ? o2.getDeclaringClass().getSimpleName() : o2.getName()).append('('); 1244 Class<?>[] pt = o2.getParameterTypes(); 1245 for (int i = 0; i < pt.length; i++) { 1246 if (i > 0) 1247 sb.append(','); 1248 sb.append(pt[i].getSimpleName()); 1249 } 1250 sb.append(')'); 1251 return sb.toString(); 1252 } 1253 if (isArray(o)) { 1254 var l = list(); 1255 for (var i = 0; i < Array.getLength(o); i++) { 1256 l.add(Array.get(o, i)); 1257 } 1258 return r(l); 1259 } 1260 return o.toString(); 1261 } 1262 1263 /** 1264 * Creates a {@link RuntimeException}. 1265 * 1266 * @param msg The exception message. 1267 * @param args The arguments to substitute into the message. 1268 * @return A new RuntimeException with the formatted message. 1269 */ 1270 public static RuntimeException runtimeException(String msg, Object...args) { 1271 return new RuntimeException(args.length == 0 ? msg : f(msg, args)); 1272 } 1273 1274 /** 1275 * Shortcut for converting an object to a string. 1276 * 1277 * @param val The object to convert. 1278 * @return The string representation of the object, or <jk>null</jk> if the object is <jk>null</jk>. 1279 */ 1280 public static String s(Object val) { 1281 return val == null ? null : val.toString(); 1282 } 1283 1284 /** 1285 * Runs a snippet of code and encapsulates any throwable inside a {@link RuntimeException}. 1286 * 1287 * @param snippet The snippet of code to run. 1288 */ 1289 public static void safe(Snippet snippet) { 1290 try { 1291 snippet.run(); 1292 } catch (RuntimeException t) { 1293 throw t; 1294 } catch (Throwable t) { 1295 throw ThrowableUtils.asRuntimeException(t); 1296 } 1297 } 1298 1299 /** 1300 * Used to wrap code that returns a value but throws an exception. 1301 * Useful in cases where you're trying to execute code in a fluent method call 1302 * or are trying to eliminate untestable catch blocks in code. 1303 * 1304 * @param <T> The return type. 1305 * @param s The supplier that may throw an exception. 1306 * @return The result of the supplier execution. 1307 */ 1308 public static <T> T safe(ThrowingSupplier<T> s) { 1309 try { 1310 return s.get(); 1311 } catch (RuntimeException e) { 1312 throw e; 1313 } catch (Exception e) { 1314 throw new RuntimeException(e); 1315 } 1316 } 1317 1318 /** 1319 * Allows you to wrap a supplier that throws an exception so that it can be used in a fluent interface. 1320 * 1321 * @param <T> The supplier type. 1322 * @param supplier The supplier throwing an exception. 1323 * @return The supplied result. 1324 * @throws RuntimeException if supplier threw an exception. 1325 */ 1326 public static <T> T safeSupplier(ThrowableUtils.SupplierWithThrowable<T> supplier) { 1327 try { 1328 return supplier.get(); 1329 } catch (RuntimeException t) { 1330 throw t; 1331 } catch (Throwable t) { 1332 throw ThrowableUtils.asRuntimeException(t); 1333 } 1334 } 1335 1336 /** 1337 * Shortcut for creating a modifiable set out of an array of values. 1338 * 1339 * @param <T> The element type. 1340 * @param values The values to add to the set. 1341 * @return A modifiable LinkedHashSet containing the specified values. 1342 */ 1343 @SafeVarargs 1344 public static <T> LinkedHashSet<T> set(T...values) { // NOSONAR 1345 return new LinkedHashSet<>(Arrays.asList(values)); 1346 } 1347 1348 /** 1349 * Splits a comma-delimited list into a list of strings. 1350 * 1351 * @param s The string to split. 1352 * @return A list of split strings, or an empty list if the input is <jk>null</jk>. 1353 */ 1354 public static List<String> split(String s) { 1355 return s == null ? Collections.emptyList() : split(s, ','); 1356 } 1357 1358 /** 1359 * Splits a character-delimited string into a string array. 1360 * 1361 * <p> 1362 * Does not split on escaped-delimiters (e.g. "\,"); 1363 * Resulting tokens are trimmed of whitespace. 1364 * 1365 * <p> 1366 * <b>NOTE:</b> This behavior is different than the Jakarta equivalent. 1367 * split("a,b,c",',') -> {"a","b","c"} 1368 * split("a, b ,c ",',') -> {"a","b","c"} 1369 * split("a,,c",',') -> {"a","","c"} 1370 * split(",,",',') -> {"","",""} 1371 * split("",',') -> {} 1372 * split(null,',') -> null 1373 * split("a,b\,c,d", ',', false) -> {"a","b\,c","d"} 1374 * split("a,b\\,c,d", ',', false) -> {"a","b\","c","d"} 1375 * split("a,b\,c,d", ',', true) -> {"a","b,c","d"} 1376 * 1377 * @param s The string to split. Can be <jk>null</jk>. 1378 * @param c The character to split on. 1379 * @return The tokens, or <jk>null</jk> if the string was null. 1380 */ 1381 public static List<String> split(String s, char c) { 1382 return split(s, c, Integer.MAX_VALUE); 1383 } 1384 1385 /** 1386 * Same as {@link #splita(String,char)} but consumes the tokens instead of creating an array. 1387 * 1388 * @param s The string to split. 1389 * @param c The character to split on. 1390 * @param consumer The consumer of the tokens. 1391 */ 1392 public static void split(String s, char c, Consumer<String> consumer) { 1393 var escapeChars = StringUtils.getEscapeSet(c); 1394 1395 if (isEmpty(s)) 1396 return; 1397 if (s.indexOf(c) == -1) { 1398 consumer.accept(s); 1399 return; 1400 } 1401 1402 var x1 = 0; 1403 var escapeCount = 0; 1404 1405 for (var i = 0; i < s.length(); i++) { 1406 if (s.charAt(i) == '\\') 1407 escapeCount++; 1408 else if (s.charAt(i)==c && escapeCount % 2 == 0) { 1409 var s2 = s.substring(x1, i); 1410 var s3 = StringUtils.unEscapeChars(s2, escapeChars); 1411 consumer.accept(s3.trim()); // NOSONAR - NPE not possible. 1412 x1 = i+1; 1413 } 1414 if (s.charAt(i) != '\\') 1415 escapeCount = 0; 1416 } 1417 var s2 = s.substring(x1); 1418 var s3 = StringUtils.unEscapeChars(s2, escapeChars); 1419 consumer.accept(s3.trim()); // NOSONAR - NPE not possible. 1420 } 1421 1422 /** 1423 * Same as {@link #splita(String, char)} but limits the number of tokens returned. 1424 * 1425 * @param s The string to split. Can be <jk>null</jk>. 1426 * @param c The character to split on. 1427 * @param limit The maximum number of tokens to return. 1428 * @return The tokens, or <jk>null</jk> if the string was null. 1429 */ 1430 public static List<String> split(String s, char c, int limit) { 1431 1432 var escapeChars = StringUtils.getEscapeSet(c); 1433 1434 if (s == null) 1435 return null; // NOSONAR - Intentional. 1436 if (isEmpty(s)) 1437 return Collections.emptyList(); 1438 if (s.indexOf(c) == -1) 1439 return Collections.singletonList(s); 1440 1441 var l = new LinkedList<String>(); 1442 var sArray = s.toCharArray(); 1443 var x1 = 0; 1444 var escapeCount = 0; 1445 limit--; 1446 for (var i = 0; i < sArray.length && limit > 0; i++) { 1447 if (sArray[i] == '\\') 1448 escapeCount++; 1449 else if (sArray[i]==c && escapeCount % 2 == 0) { 1450 var s2 = new String(sArray, x1, i-x1); 1451 var s3 = StringUtils.unEscapeChars(s2, escapeChars); 1452 l.add(s3.trim()); 1453 limit--; 1454 x1 = i+1; 1455 } 1456 if (sArray[i] != '\\') 1457 escapeCount = 0; 1458 } 1459 var s2 = new String(sArray, x1, sArray.length-x1); 1460 var s3 = StringUtils.unEscapeChars(s2, escapeChars); 1461 l.add(s3.trim()); 1462 1463 return l; 1464 } 1465 1466 /** 1467 * Same as {@link #splita(String)} but consumes the tokens instead of creating an array. 1468 * 1469 * @param s The string to split. 1470 * @param consumer The consumer of the tokens. 1471 */ 1472 public static void split(String s, Consumer<String> consumer) { 1473 split(s, ',', consumer); 1474 } 1475 1476 /** 1477 * Splits a comma-delimited list into an array of strings. 1478 * 1479 * @param s The string to split. 1480 * @return An array of split strings. 1481 */ 1482 public static String[] splita(String s) { 1483 return splita(s, ','); 1484 } 1485 1486 /** 1487 * Splits a character-delimited string into a string array. 1488 * 1489 * <p> 1490 * Does not split on escaped-delimiters (e.g. "\,"); 1491 * Resulting tokens are trimmed of whitespace. 1492 * 1493 * <p> 1494 * <b>NOTE:</b> This behavior is different than the Jakarta equivalent. 1495 * split("a,b,c",',') -> {"a","b","c"} 1496 * split("a, b ,c ",',') -> {"a","b","c"} 1497 * split("a,,c",',') -> {"a","","c"} 1498 * split(",,",',') -> {"","",""} 1499 * split("",',') -> {} 1500 * split(null,',') -> null 1501 * split("a,b\,c,d", ',', false) -> {"a","b\,c","d"} 1502 * split("a,b\\,c,d", ',', false) -> {"a","b\","c","d"} 1503 * split("a,b\,c,d", ',', true) -> {"a","b,c","d"} 1504 * 1505 * @param s The string to split. Can be <jk>null</jk>. 1506 * @param c The character to split on. 1507 * @return The tokens, or <jk>null</jk> if the string was null. 1508 */ 1509 public static String[] splita(String s, char c) { 1510 return splita(s, c, Integer.MAX_VALUE); 1511 } 1512 1513 /** 1514 * Same as {@link #splita(String, char)} but limits the number of tokens returned. 1515 * 1516 * @param s The string to split. Can be <jk>null</jk>. 1517 * @param c The character to split on. 1518 * @param limit The maximum number of tokens to return. 1519 * @return The tokens, or <jk>null</jk> if the string was null. 1520 */ 1521 public static String[] splita(String s, char c, int limit) { 1522 var l = split(s, c, limit); 1523 return l == null ? null : l.toArray(new String[l.size()]); 1524 } 1525 1526 /** 1527 * Same as {@link #splita(String, char)} except splits all strings in the input and returns a single result. 1528 * 1529 * @param s The string to split. Can be <jk>null</jk>. 1530 * @param c The character to split on. 1531 * @return The tokens, or null if the input array was null 1532 */ 1533 public static String[] splita(String[] s, char c) { 1534 if (s == null) 1535 return null; // NOSONAR - Intentional. 1536 var l = new LinkedList<String>(); 1537 for (var ss : s) { 1538 if (ss == null || ss.indexOf(c) == -1) 1539 l.add(ss); 1540 else 1541 Collections.addAll(l, splita(ss, c)); 1542 } 1543 return l.toArray(new String[l.size()]); 1544 } 1545 1546 /** 1547 * Splits a list of key-value pairs into an ordered map. 1548 * 1549 * <p> 1550 * Example: 1551 * <p class='bjava'> 1552 * String <jv>in</jv> = <js>"foo=1;bar=2"</js>; 1553 * Map <jv>map</jv> = StringUtils.<jsm>splitMap</jsm>(in, <js>';'</js>, <js>'='</js>, <jk>true</jk>); 1554 * </p> 1555 * 1556 * @param s The string to split. 1557 * @param trim Trim strings after parsing. 1558 * @return The parsed map, or null if the string was null. 1559 */ 1560 public static Map<String,String> splitMap(String s, boolean trim) { 1561 1562 if (s == null) 1563 return null; // NOSONAR - Intentional. 1564 if (isEmpty(s)) 1565 return Collections.emptyMap(); 1566 1567 var m = new LinkedHashMap<String,String>(); 1568 1569 final int 1570 S1 = 1, // Found start of key, looking for equals. 1571 S2 = 2; // Found equals, looking for delimiter (or end). 1572 1573 var state = S1; 1574 1575 var sArray = s.toCharArray(); 1576 var x1 = 0; 1577 var escapeCount = 0; 1578 String key = null; 1579 for (var i = 0; i < sArray.length + 1; i++) { 1580 var c = i == sArray.length ? ',' : sArray[i]; 1581 if (c == '\\') 1582 escapeCount++; 1583 if (escapeCount % 2 == 0) { 1584 if (state == S1) { 1585 if (c == '=') { 1586 key = s.substring(x1, i); 1587 if (trim) 1588 key = StringUtils.trim(key); 1589 key = StringUtils.unEscapeChars(key, StringUtils.MAP_ESCAPE_SET); 1590 state = S2; 1591 x1 = i+1; 1592 } else if (c == ',') { 1593 key = s.substring(x1, i); 1594 if (trim) 1595 key = StringUtils.trim(key); 1596 key = StringUtils.unEscapeChars(key, StringUtils.MAP_ESCAPE_SET); 1597 m.put(key, ""); 1598 state = S1; 1599 x1 = i+1; 1600 } 1601 } else if (state == S2) { 1602 if (c == ',') { // NOSONAR - Intentional. 1603 var val = s.substring(x1, i); 1604 if (trim) 1605 val = StringUtils.trim(val); 1606 val = StringUtils.unEscapeChars(val, StringUtils.MAP_ESCAPE_SET); 1607 m.put(key, val); 1608 key = null; 1609 x1 = i+1; 1610 state = S1; 1611 } 1612 } 1613 } 1614 if (c != '\\') 1615 escapeCount = 0; 1616 } 1617 1618 return m; 1619 } 1620 1621 /** 1622 * Splits the method arguments in the signature of a method. 1623 * 1624 * @param s The arguments to split. 1625 * @return The split arguments, or null if the input string is null. 1626 */ 1627 public static String[] splitMethodArgs(String s) { 1628 1629 if (s == null) 1630 return null; // NOSONAR - Intentional. 1631 if (isEmpty(s)) 1632 return new String[0]; 1633 if (s.indexOf(',') == -1) 1634 return new String[]{s}; 1635 1636 var l = new LinkedList<String>(); 1637 var sArray = s.toCharArray(); 1638 var x1 = 0; 1639 var paramDepth = 0; 1640 1641 for (var i = 0; i < sArray.length; i++) { 1642 var c = s.charAt(i); 1643 if (c == '>') 1644 paramDepth++; 1645 else if (c == '<') 1646 paramDepth--; 1647 else if (c == ',' && paramDepth == 0) { 1648 var s2 = new String(sArray, x1, i-x1); 1649 l.add(s2.trim()); 1650 x1 = i+1; 1651 } 1652 } 1653 1654 var s2 = new String(sArray, x1, sArray.length-x1); 1655 l.add(s2.trim()); 1656 1657 return l.toArray(new String[l.size()]); 1658 } 1659 1660 /** 1661 * Splits a comma-delimited list containing "nesting constructs". 1662 * 1663 * Nesting constructs are simple embedded "{...}" comma-delimted lists. 1664 * 1665 * Example: 1666 * "a{b,c},d" -> ["a{b,c}","d"] 1667 * 1668 * Handles escapes and trims whitespace from tokens. 1669 * 1670 * @param s The input string. 1671 * The results, or <jk>null</jk> if the input was <jk>null</jk>. 1672 * <br>An empty string results in an empty array. 1673 */ 1674 public static List<String> splitNested(String s) { 1675 var escapeChars = StringUtils.getEscapeSet(','); 1676 1677 if (s == null) return null; // NOSONAR - Intentional. 1678 if (isEmpty(s)) return Collections.emptyList(); 1679 if (s.indexOf(',') == -1) return Collections.singletonList(StringUtils.trim(s)); 1680 1681 var l = new LinkedList<String>(); 1682 1683 var x1 = 0; 1684 var inEscape = false; 1685 var depthCount = 0; 1686 1687 for (var i = 0; i < s.length(); i++) { 1688 var c = s.charAt(i); 1689 if (inEscape) { 1690 if (c == '\\') { 1691 inEscape = false; 1692 } 1693 } else { 1694 if (c == '\\') { 1695 inEscape = true; 1696 } else if (c == '{') { 1697 depthCount++; 1698 } else if (c == '}') { 1699 depthCount--; 1700 } else if (c == ',' && depthCount == 0) { 1701 l.add(StringUtils.trim(StringUtils.unEscapeChars(s.substring(x1, i), escapeChars))); 1702 x1 = i+1; 1703 } 1704 } 1705 } 1706 l.add(StringUtils.trim(StringUtils.unEscapeChars(s.substring(x1, s.length()), escapeChars))); 1707 1708 return l; 1709 } 1710 1711 /** 1712 * Splits a nested comma-delimited list. 1713 * 1714 * Nesting constructs are simple embedded "{...}" comma-delimted lists. 1715 * 1716 * Example: 1717 * "a{b,c{d,e}}" -> ["b","c{d,e}"] 1718 * 1719 * Handles escapes and trims whitespace from tokens. 1720 * 1721 * @param s The input string. 1722 * The results, or <jk>null</jk> if the input was <jk>null</jk>. 1723 * <br>An empty string results in an empty array. 1724 */ 1725 public static List<String> splitNestedInner(String s) { 1726 if (s == null) throw illegalArg("String was null."); 1727 if (isEmpty(s)) throw illegalArg("String was empty."); 1728 1729 final int 1730 S1 = 1, // Looking for '{' 1731 S2 = 2; // Found '{', looking for '}' 1732 1733 var start = -1; 1734 var end = -1; 1735 var state = S1; 1736 var depth = 0; 1737 var inEscape = false; 1738 1739 for (var i = 0; i < s.length(); i++) { 1740 var c = s.charAt(i); 1741 if (inEscape) { 1742 if (c == '\\') { 1743 inEscape = false; 1744 } 1745 } else { 1746 if (c == '\\') { 1747 inEscape = true; 1748 } else if (state == S1) { 1749 if (c == '{') { 1750 start = i+1; 1751 state = S2; 1752 } 1753 } else /* state == S2 */ { 1754 if (c == '{') { 1755 depth++; 1756 } else if (depth > 0 && c == '}') { 1757 depth--; 1758 } else if (c == '}') { 1759 end = i; 1760 break; 1761 } 1762 } 1763 } 1764 } 1765 1766 if (start == -1) throw illegalArg("Start character '{' not found in string.", s); 1767 if (end == -1) throw illegalArg("End character '}' not found in string.", s); 1768 return splitNested(s.substring(start, end)); 1769 } 1770 1771 /** 1772 * Splits a space-delimited string with optionally quoted arguments. 1773 * 1774 * <p> 1775 * Examples: 1776 * <ul> 1777 * <li><js>"foo"</js> => <c>["foo"]</c> 1778 * <li><js>" foo "</js> => <c>["foo"]</c> 1779 * <li><js>"foo bar baz"</js> => <c>["foo","bar","baz"]</c> 1780 * <li><js>"foo 'bar baz'"</js> => <c>["foo","bar baz"]</c> 1781 * <li><js>"foo \"bar baz\""</js> => <c>["foo","bar baz"]</c> 1782 * <li><js>"foo 'bar\'baz'"</js> => <c>["foo","bar'baz"]</c> 1783 * </ul> 1784 * 1785 * @param s The input string. 1786 * @return 1787 * The results, or <jk>null</jk> if the input was <jk>null</jk>. 1788 * <br>An empty string results in an empty array. 1789 */ 1790 public static String[] splitQuoted(String s) { 1791 return splitQuoted(s, false); 1792 } 1793 1794 /** 1795 * Same as {@link #splitQuoted(String)} but allows you to optionally keep the quote characters. 1796 * 1797 * @param s The input string. 1798 * @param keepQuotes If <jk>true</jk>, quote characters are kept on the tokens. 1799 * @return 1800 * The results, or <jk>null</jk> if the input was <jk>null</jk>. 1801 * <br>An empty string results in an empty array. 1802 */ 1803 public static String[] splitQuoted(String s, boolean keepQuotes) { 1804 1805 if (s == null) 1806 return null; // NOSONAR - Intentional. 1807 1808 s = s.trim(); 1809 1810 if (isEmpty(s)) 1811 return new String[0]; 1812 1813 if (! StringUtils.containsAny(s, ' ', '\t', '\'', '"')) 1814 return new String[]{s}; 1815 1816 final int 1817 S1 = 1, // Looking for start of token. 1818 S2 = 2, // Found ', looking for end ' 1819 S3 = 3, // Found ", looking for end " 1820 S4 = 4; // Found non-whitespace, looking for end whitespace. 1821 1822 var state = S1; 1823 1824 var isInEscape = false; 1825 var needsUnescape = false; 1826 var mark = 0; 1827 1828 var l = new ArrayList<String>(); 1829 for (var i = 0; i < s.length(); i++) { 1830 var c = s.charAt(i); 1831 1832 if (state == S1) { 1833 if (c == '\'') { 1834 state = S2; 1835 mark = keepQuotes ? i : i+1; 1836 } else if (c == '"') { 1837 state = S3; 1838 mark = keepQuotes ? i : i+1; 1839 } else if (c != ' ' && c != '\t') { 1840 state = S4; 1841 mark = i; 1842 } 1843 } else if (state == S2 || state == S3) { 1844 if (c == '\\') { 1845 isInEscape = ! isInEscape; 1846 needsUnescape = ! keepQuotes; 1847 } else if (! isInEscape) { 1848 if (c == (state == S2 ? '\'' : '"')) { 1849 var s2 = s.substring(mark, keepQuotes ? i+1 : i); 1850 if (needsUnescape) // NOSONAR - False positive check. 1851 s2 = StringUtils.unEscapeChars(s2, StringUtils.QUOTE_ESCAPE_SET); 1852 l.add(s2); 1853 state = S1; 1854 isInEscape = needsUnescape = false; 1855 } 1856 } else { 1857 isInEscape = false; 1858 } 1859 } else /* state == S4 */ { 1860 if (c == ' ' || c == '\t') { 1861 l.add(s.substring(mark, i)); 1862 state = S1; 1863 } 1864 } 1865 } 1866 if (state == S4) 1867 l.add(s.substring(mark)); 1868 else if (state == S2 || state == S3) 1869 throw new IllegalArgumentException("Unmatched string quotes: " + s); 1870 return l.toArray(new String[l.size()]); 1871 } 1872 1873 /** 1874 * Converts various collection-like objects to a {@link List}. 1875 * 1876 * <p>This utility method enables testing of any collection-like object by converting it to a List that can be 1877 * passed to methods such as TestUtils.assertList().</p> 1878 * 1879 * <h5 class='section'>Supported Input Types:</h5> 1880 * <ul> 1881 * <li><b>List:</b> Returns the input unchanged</li> 1882 * <li><b>Iterable:</b> Any collection, set, queue, etc. (converted to List preserving order)</li> 1883 * <li><b>Iterator:</b> Converts iterator contents to List</li> 1884 * <li><b>Enumeration:</b> Converts enumeration contents to List</li> 1885 * <li><b>Stream:</b> Converts stream contents to List (stream is consumed)</li> 1886 * <li><b>Map:</b> Converts map entries to List of Map.Entry objects</li> 1887 * <li><b>Array:</b> Converts any array type (including primitive arrays) to List</li> 1888 * </ul> 1889 * 1890 * <h5 class='section'>Usage Examples:</h5> 1891 * <p class='bjava'> 1892 * <jc>// Test a Set</jc> 1893 * Set<String> <jv>mySet</jv> = Set.of(<js>"a"</js>, <js>"b"</js>, <js>"c"</js>); 1894 * assertList(toList(<jv>mySet</jv>), <js>"a"</js>, <js>"b"</js>, <js>"c"</js>); 1895 * 1896 * <jc>// Test an array</jc> 1897 * String[] <jv>myArray</jv> = {<js>"x"</js>, <js>"y"</js>, <js>"z"</js>}; 1898 * assertList(toList(<jv>myArray</jv>), <js>"x"</js>, <js>"y"</js>, <js>"z"</js>); 1899 * 1900 * <jc>// Test a primitive array</jc> 1901 * <jk>int</jk>[] <jv>numbers</jv> = {1, 2, 3}; 1902 * assertList(toList(<jv>numbers</jv>), <js>"1"</js>, <js>"2"</js>, <js>"3"</js>); 1903 * 1904 * <jc>// Test a Stream</jc> 1905 * Stream<String> <jv>myStream</jv> = Stream.of(<js>"foo"</js>, <js>"bar"</js>); 1906 * assertList(toList(<jv>myStream</jv>), <js>"foo"</js>, <js>"bar"</js>); 1907 * 1908 * <jc>// Test a Map (converted to entries)</jc> 1909 * Map<String,Integer> <jv>myMap</jv> = Map.of(<js>"a"</js>, 1, <js>"b"</js>, 2); 1910 * assertList(toList(<jv>myMap</jv>), <js>"a=1"</js>, <js>"b=2"</js>); 1911 * 1912 * <jc>// Test any Iterable collection</jc> 1913 * Queue<String> <jv>myQueue</jv> = new LinkedList<>(List.of(<js>"first"</js>, <js>"second"</js>)); 1914 * assertList(toList(<jv>myQueue</jv>), <js>"first"</js>, <js>"second"</js>); 1915 * </p> 1916 * 1917 * <h5 class='section'>Integration with Testing:</h5> 1918 * <p>This method is specifically designed to work with testing frameworks to provide 1919 * a unified testing approach for all collection-like types. Instead of having separate assertion methods 1920 * for arrays, sets, and other collections, you can convert them all to Lists and use standard 1921 * list assertion methods.</p> 1922 * 1923 * @param o The object to convert to a List. Must not be null and must be a supported collection-like type. 1924 * @return A {@link List} containing the elements from the input object. 1925 * @throws IllegalArgumentException if the input object cannot be converted to a List. 1926 * @see #arrayToList(Object) 1927 */ 1928 public static final List<?> toList(Object o) { // NOSONAR 1929 assertArgNotNull("o", o); 1930 if (o instanceof List<?> o2) return o2; 1931 if (o instanceof Iterable<?> o2) return StreamSupport.stream(o2.spliterator(), false).toList(); 1932 if (o instanceof Iterator<?> o2) return StreamSupport.stream(Spliterators.spliteratorUnknownSize(o2, 0), false).toList(); 1933 if (o instanceof Enumeration<?> o2) return Collections.list(o2); 1934 if (o instanceof Stream<?> o2) return o2.toList(); 1935 if (o instanceof Map<?,?> o2) return toList(o2.entrySet()); 1936 if (o instanceof Optional<?> o2) return o2.isEmpty() ? Collections.emptyList() : Collections.singletonList(o2.get()); 1937 if (isArray(o)) return arrayToList(o); 1938 throw runtimeException("Could not convert object of type {0} to a list", classNameOf(o)); 1939 } 1940 1941 public static boolean isConvertibleToList(Object o) { 1942 return o != null && (o instanceof Collection || o instanceof Iterable || o instanceof Iterator || o instanceof Enumeration || o instanceof Stream || o instanceof Map || o instanceof Optional || isArray(o)); 1943 } 1944 1945 /** 1946 * Converts an array to a stream of objects. 1947 * @param array The array to convert. 1948 * @return A new stream. 1949 */ 1950 public static Stream<Object> toStream(Object array) { 1951 assertArg(isArray(array), "Arg was not an array. Type: {0}", array.getClass().getName()); 1952 var length = Array.getLength(array); 1953 return IntStream.range(0, length).mapToObj(i -> Array.get(array, i)); 1954 } 1955 1956 /** 1957 * Converts a string to the specified type using registered conversion functions. 1958 * 1959 * @param <T> The target type. 1960 * @param s The string to convert. 1961 * @param def The default value (used to determine the target type). 1962 * @return The converted value, or <jk>null</jk> if the string or default is <jk>null</jk>. 1963 * @throws RuntimeException If the type is not supported for conversion. 1964 */ 1965 @SuppressWarnings("rawtypes") 1966 private static <T> T toType(String s, T def) { 1967 if (s == null || def == null) 1968 return null; 1969 var c = (Class<T>)def.getClass(); 1970 if (c == String.class) 1971 return (T)s; 1972 if (c.isEnum()) 1973 return (T)Enum.valueOf((Class<? extends Enum>) c, s); 1974 var f = (Function<String,T>)ENV_FUNCTIONS.get(c); 1975 if (f == null) 1976 throw runtimeException("Invalid env type: {0}", c); 1977 return f.apply(s); 1978 } 1979 1980 /** 1981 * Traverses all elements in the specified object and executes a consumer for it. 1982 * 1983 * @param <T> The element type. 1984 * @param o The object to traverse. 1985 * @param c The consumer of the objects. 1986 */ 1987 public static <T> void traverse(Object o, Consumer<T> c) { 1988 if (o == null) 1989 return; 1990 if (o instanceof Iterable<?> o2) 1991 o2.forEach(x -> traverse(x, c)); 1992 else if (o instanceof Stream<?> o2) 1993 o2.forEach(x -> traverse(x, c)); 1994 else if (isArray(o)) 1995 toStream(o).forEach(x -> traverse(x, c)); 1996 else 1997 c.accept((T)o); 1998 } 1999 2000 /** 2001 * Creates an unmodifiable view of the specified list. 2002 * 2003 * <p>This is a null-safe wrapper around {@link Collections#unmodifiableList(List)}.</p> 2004 * 2005 * @param <T> The element type. 2006 * @param value The list to make unmodifiable. Can be null. 2007 * @return An unmodifiable view of the list, or null if the input was null. 2008 */ 2009 public static <T> List<T> u(List<? extends T> value) { 2010 return value == null ? null : Collections.unmodifiableList(value); 2011 } 2012 2013 /** 2014 * Creates an unmodifiable view of the specified map. 2015 * 2016 * <p>This is a null-safe wrapper around {@link Collections#unmodifiableMap(Map)}.</p> 2017 * 2018 * @param <K> The key type. 2019 * @param <V> The value type. 2020 * @param value The map to make unmodifiable. Can be null. 2021 * @return An unmodifiable view of the map, or null if the input was null. 2022 */ 2023 public static <K,V> Map<K,V> u(Map<? extends K, ? extends V> value) { 2024 return value == null ? null : Collections.unmodifiableMap(value); 2025 } 2026 2027 /** 2028 * Creates an unmodifiable view of the specified set. 2029 * 2030 * <p>This is a null-safe wrapper around {@link Collections#unmodifiableSet(Set)}.</p> 2031 * 2032 * @param <T> The element type. 2033 * @param value The set to make unmodifiable. Can be null. 2034 * @return An unmodifiable view of the set, or null if the input was null. 2035 */ 2036 public static <T> Set<T> u(Set<? extends T> value) { 2037 return value == null ? null : Collections.unmodifiableSet(value); 2038 } 2039 2040 /** Constructor - This class is meant to be subclasses. */ 2041 protected Utils() {} 2042 2043 /** 2044 * Helper method for creating StringBuilder objects. 2045 * 2046 * @param value The string value to wrap in a StringBuilder. 2047 * @return A new StringBuilder containing the specified value. 2048 */ 2049 public static StringBuilder sb(String value) { 2050 return new StringBuilder(value); 2051 } 2052 2053 /** 2054 * Gets the fully qualified class name of the specified object. 2055 * 2056 * @param o The object to get the class name for. 2057 * @return The fully qualified class name, or <jk>null</jk> if the object is <jk>null</jk>. 2058 */ 2059 public static String classNameOf(Object o) { 2060 return o == null ? null : o.getClass().getName(); 2061 } 2062 2063 /** 2064 * Gets the simple class name of the specified object. 2065 * 2066 * @param o The object to get the simple class name for. 2067 * @return The simple class name, or <jk>null</jk> if the object is <jk>null</jk>. 2068 */ 2069 public static String simpleClassNameOf(Object o) { 2070 return o == null ? null : o.getClass().getSimpleName(); 2071 } 2072}