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.commons.utils; 018 019import static org.apache.juneau.commons.reflect.ReflectionUtils.*; 020import static org.apache.juneau.commons.utils.ObjectUtils.*; 021import static org.apache.juneau.commons.utils.Shorts.*; 022import static org.apache.juneau.commons.utils.ThrowableUtils.*; 023 024import java.lang.reflect.*; 025import java.util.*; 026import java.util.function.*; 027 028import org.apache.juneau.commons.collections.*; 029import org.apache.juneau.commons.lang.*; 030import org.apache.juneau.commons.reflect.*; 031 032/** 033 * Utility methods for working with classes. 034 * 035 */ 036@SuppressWarnings({ 037 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 038 "java:S3011", // Reflection access needed for class introspection utilities 039 "java:S3740", // Raw Class/Constructor types used intentionally for generic class-loading and reflection utilities 040 "rawtypes" // The modifiableCollectionTypes cache, public isVoid/isNotVoid(Class) and instanceof Class patterns use the raw Class type 041}) 042public class ClassUtils { 043 044 /** 045 * Prevents instantiation. 046 */ 047 private ClassUtils() {} 048 049 /** 050 * Predicate check to filter out void classes. 051 */ 052 public static final Predicate<Class<?>> NOT_VOID = ClassUtils::isNotVoid; 053 054 private static Cache<Class,Boolean> modifiableCollectionTypes = Cache.of(Class.class, Boolean.class).build(); 055 056 /** 057 * Determines whether the specified collection supports modification operations (e.g., {@code add()}, {@code remove()}). 058 * 059 * <p> 060 * This method performs a heuristic check based on the collection's class name to determine if it's likely modifiable. 061 * It checks whether the class name contains indicators of immutability such as "Immutable", "Unmodifiable", 062 * or "Arrays$ArrayList" (which represents the unmodifiable list returned by {@link Arrays#asList(Object...)}). 063 * 064 * <p> 065 * Results are cached for performance, so repeated calls for the same collection type are very fast. 066 * 067 * <h5 class='section'>Examples:</h5> 068 * <p class='bjava'> 069 * <jc>// Modifiable collections</jc> 070 * canAddTo(<jk>new</jk> ArrayList<>()); <jc>// true</jc> 071 * canAddTo(<jk>new</jk> LinkedList<>()); <jc>// true</jc> 072 * canAddTo(<jk>new</jk> HashSet<>()); <jc>// true</jc> 073 * 074 * <jc>// Unmodifiable collections</jc> 075 * canAddTo(Collections.unmodifiableList(...)); <jc>// false</jc> 076 * canAddTo(Collections.unmodifiableSet(...)); <jc>// false</jc> 077 * canAddTo(Arrays.asList(<js>"a"</js>, <js>"b"</js>)); <jc>// false</jc> 078 * canAddTo(List.of(<js>"a"</js>, <js>"b"</js>)); <jc>// false (ImmutableCollections)</jc> 079 * </p> 080 * 081 * <p> 082 * <b>Note:</b> This is a heuristic check based on naming conventions. It does not attempt to actually 083 * modify the collection, so it never throws exceptions. However, it may produce false positives for 084 * custom collection implementations with misleading names. 085 * 086 * @param value The collection to check. Must not be <jk>null</jk>. 087 * @return <jk>true</jk> if the collection is likely modifiable, <jk>false</jk> if it's likely unmodifiable. 088 * @throws IllegalArgumentException If value is <jk>null</jk>. 089 */ 090 public static boolean canAddTo(Collection<?> value) { 091 reqnn("value", value); 092 return canAddTo(value.getClass()); 093 } 094 095 /** 096 * Determines whether the specified map supports modification operations (e.g., {@code put()}, {@code remove()}). 097 * 098 * <p> 099 * This method performs a heuristic check based on the map's class name to determine if it's likely modifiable. 100 * It checks whether the class name contains indicators of immutability such as "Immutable", "Unmodifiable", 101 * or "Arrays$ArrayList" (which represents unmodifiable collections). 102 * 103 * <p> 104 * Results are cached for performance, so repeated calls for the same map type are very fast. 105 * 106 * <h5 class='section'>Examples:</h5> 107 * <p class='bjava'> 108 * <jc>// Modifiable maps</jc> 109 * canPutTo(<jk>new</jk> HashMap<>()); <jc>// true</jc> 110 * canPutTo(<jk>new</jk> LinkedHashMap<>()); <jc>// true</jc> 111 * canPutTo(<jk>new</jk> TreeMap<>()); <jc>// true</jc> 112 * 113 * <jc>// Unmodifiable maps</jc> 114 * canPutTo(Collections.unmodifiableMap(...)); <jc>// false</jc> 115 * canPutTo(Map.of(<js>"key"</js>, <js>"value"</js>)); <jc>// false (ImmutableCollections)</jc> 116 * </p> 117 * 118 * <p> 119 * <b>Note:</b> This is a heuristic check based on naming conventions. It does not attempt to actually 120 * modify the map, so it never throws exceptions. However, it may produce false positives for 121 * custom map implementations with misleading names. 122 * 123 * @param value The map to check. Must not be <jk>null</jk>. 124 * @return <jk>true</jk> if the map is likely modifiable, <jk>false</jk> if it's likely unmodifiable. 125 * @throws IllegalArgumentException If value is <jk>null</jk>. 126 */ 127 public static boolean canPutTo(Map<?,?> value) { 128 reqnn("value", value); 129 return canAddTo(value.getClass()); 130 } 131 132 /** 133 * Returns the fully-qualified class name for the specified object. 134 * 135 * <p> 136 * This method returns the canonical JVM class name including the full package path. 137 * 138 * <h5 class='section'>Examples:</h5> 139 * <p class='bjava'> 140 * <jc>// Regular classes</jc> 141 * className(String.<jk>class</jk>); <jc>// "java.lang.String"</jc> 142 * className(<jk>new</jk> HashMap<>()); <jc>// "java.util.HashMap"</jc> 143 * 144 * <jc>// Inner classes</jc> 145 * className(Map.Entry.<jk>class</jk>); <jc>// "java.util.Map$Entry"</jc> 146 * 147 * <jc>// Primitives</jc> 148 * className(<jk>int</jk>.<jk>class</jk>); <jc>// "int"</jc> 149 * className(<jk>boolean</jk>.<jk>class</jk>); <jc>// "boolean"</jc> 150 * 151 * <jc>// Arrays</jc> 152 * className(String[].<jk>class</jk>); <jc>// "[Ljava.lang.String;"</jc> 153 * className(<jk>int</jk>[].<jk>class</jk>); <jc>// "[I"</jc> 154 * className(String[][].<jk>class</jk>); <jc>// "[[Ljava.lang.String;"</jc> 155 * 156 * <jc>// Null</jc> 157 * className(<jk>null</jk>); <jc>// null</jc> 158 * </p> 159 * 160 * @param value The object to get the class name for. 161 * @return The name of the class or <jk>null</jk> if the value was null. 162 */ 163 public static String className(Object value) { 164 if (value == null) 165 return null; 166 if (value instanceof Class value2) 167 return value2.getName(); 168 if (value instanceof ClassInfo value2) 169 return value2.getName(); 170 return value.getClass().getName(); 171 } 172 173 /** 174 * Returns the simple (non-qualified) class name for the specified object. 175 * 176 * <p> 177 * This method returns only the simple class name without any package or outer class information. 178 * For inner classes, only the innermost class name is returned. 179 * 180 * <h5 class='section'>Examples:</h5> 181 * <p class='bjava'> 182 * <jc>// Regular classes</jc> 183 * simpleClassName(String.<jk>class</jk>); <jc>// "String"</jc> 184 * simpleClassName(<jk>new</jk> HashMap<>()); <jc>// "HashMap"</jc> 185 * 186 * <jc>// Inner classes</jc> 187 * simpleClassName(Map.Entry.<jk>class</jk>); <jc>// "Entry"</jc> 188 * 189 * <jc>// Primitives</jc> 190 * simpleClassName(<jk>int</jk>.<jk>class</jk>); <jc>// "int"</jc> 191 * simpleClassName(<jk>boolean</jk>.<jk>class</jk>); <jc>// "boolean"</jc> 192 * 193 * <jc>// Arrays</jc> 194 * simpleClassName(String[].<jk>class</jk>); <jc>// "String[]"</jc> 195 * simpleClassName(<jk>int</jk>[].<jk>class</jk>); <jc>// "int[]"</jc> 196 * simpleClassName(String[][].<jk>class</jk>); <jc>// "String[][]"</jc> 197 * 198 * <jc>// Null</jc> 199 * simpleClassName(<jk>null</jk>); <jc>// null</jc> 200 * </p> 201 * 202 * @param value The object to get the simple class name for. 203 * @return The simple name of the class or <jk>null</jk> if the value was null. 204 */ 205 public static String classNameSimple(Object value) { 206 if (value == null) 207 return null; 208 if (value instanceof Class value2) 209 return value2.getSimpleName(); 210 if (value instanceof ClassInfo value2) 211 return value2.getNameSimple(); 212 return value.getClass().getSimpleName(); 213 } 214 215 /** 216 * Returns the simple qualified class name for the specified object. 217 * 218 * <p> 219 * This returns the simple class name including outer class names, but without the package. 220 * Inner class separators ($) are replaced with dots (.). 221 * Array types are properly formatted with brackets. 222 * 223 * <h5 class='section'>Examples:</h5> 224 * <p class='bjava'> 225 * <jc>// Regular classes</jc> 226 * simpleQualifiedClassName(String.<jk>class</jk>); <jc>// "String"</jc> 227 * simpleQualifiedClassName(<jk>new</jk> HashMap<>()); <jc>// "HashMap"</jc> 228 * 229 * <jc>// Inner classes</jc> 230 * simpleQualifiedClassName(Map.Entry.<jk>class</jk>); <jc>// "Map.Entry"</jc> 231 * simpleQualifiedClassName(Outer.Inner.Deep.<jk>class</jk>); <jc>// "Outer.Inner.Deep"</jc> 232 * 233 * <jc>// Primitives</jc> 234 * simpleQualifiedClassName(<jk>int</jk>.<jk>class</jk>); <jc>// "int"</jc> 235 * simpleQualifiedClassName(<jk>boolean</jk>.<jk>class</jk>); <jc>// "boolean"</jc> 236 * 237 * <jc>// Object arrays</jc> 238 * simpleQualifiedClassName(String[].<jk>class</jk>); <jc>// "String[]"</jc> 239 * simpleQualifiedClassName(Map.Entry[].<jk>class</jk>); <jc>// "Map.Entry[]"</jc> 240 * simpleQualifiedClassName(String[][].<jk>class</jk>); <jc>// "String[][]"</jc> 241 * 242 * <jc>// Primitive arrays</jc> 243 * simpleQualifiedClassName(<jk>int</jk>[].<jk>class</jk>); <jc>// "int[]"</jc> 244 * simpleQualifiedClassName(<jk>boolean</jk>[][].<jk>class</jk>); <jc>// "boolean[][]"</jc> 245 * 246 * <jc>// Null</jc> 247 * simpleQualifiedClassName(<jk>null</jk>); <jc>// null</jc> 248 * </p> 249 * 250 * @param value The object to get the simple qualified class name for. 251 * @return The simple qualified name of the class or <jk>null</jk> if the value was null. 252 */ 253 public static String classNameSimpleQualified(Object value) { 254 if (value == null) 255 return null; 256 var clazz = value instanceof Class<?> ? (Class<?>)value : value.getClass(); 257 258 // Handle array types by recursively getting component type 259 if (clazz.isArray()) { 260 return classNameSimpleQualified(clazz.getComponentType()) + "[]"; 261 } 262 263 // Handle non-array types 264 var className = clazz.getName(); 265 var lastDot = className.lastIndexOf('.'); 266 var simpleName = lastDot == -1 ? className : className.substring(lastDot + 1); 267 return simpleName.replace('$', '.'); 268 } 269 270 /** 271 * Returns the class types for the specified arguments. 272 * 273 * @param args The objects we're getting the classes of. Must not be <jk>null</jk> (unguarded — a <jk>null</jk> array throws {@link NullPointerException}); individual <jk>null</jk> elements map to <jk>null</jk> classes. 274 * @return The classes of the arguments. 275 */ 276 public static Class<?>[] getClasses(Object...args) { 277 var pt = new Class<?>[args.length]; 278 for (var i = 0; i < args.length; i++) 279 pt[i] = args[i] == null ? null : args[i].getClass(); 280 return pt; 281 } 282 283 /** 284 * Matches arguments to a list of parameter types. 285 * 286 * <p> 287 * This method intelligently matches a variable number of arguments to a fixed set of parameter types, 288 * handling cases where arguments may be provided in a different order, or where some arguments are 289 * missing or extra arguments are provided. This is particularly useful for reflective method/constructor 290 * invocation where parameter order flexibility is desired. 291 * 292 * <h5 class='section'>Matching Rules:</h5> 293 * <ul> 294 * <li>If arguments already match parameter types in order and count, they are returned as-is (fast path) 295 * <li>Otherwise, each parameter type is matched with the first compatible argument 296 * <li>Extra arguments are ignored 297 * <li>Missing parameters are left as <jk>null</jk> 298 * <li>Primitive types are automatically matched with their wrapper equivalents 299 * <li>Type hierarchy is respected (subclasses match parent parameters) 300 * </ul> 301 * 302 * <h5 class='section'>Examples:</h5> 303 * <p class='bjava'> 304 * <jc>// Already in correct order - fast path returns original array</jc> 305 * Class<?>[] types = {String.<jk>class</jk>, Integer.<jk>class</jk>}; 306 * Object[] args = {<js>"hello"</js>, 42}; 307 * Object[] result = getMatchingArgs(types, args); 308 * <jc>// Returns: ["hello", 42]</jc> 309 * 310 * <jc>// Arguments in wrong order - method reorders them</jc> 311 * Class<?>[] types = {Integer.<jk>class</jk>, String.<jk>class</jk>}; 312 * Object[] args = {<js>"hello"</js>, 42}; 313 * Object[] result = getMatchingArgs(types, args); 314 * <jc>// Returns: [42, "hello"]</jc> 315 * 316 * <jc>// Extra arguments are ignored</jc> 317 * Class<?>[] types = {String.<jk>class</jk>}; 318 * Object[] args = {<js>"hello"</js>, 42, <jk>true</jk>}; 319 * Object[] result = getMatchingArgs(types, args); 320 * <jc>// Returns: ["hello"]</jc> 321 * 322 * <jc>// Missing arguments become null</jc> 323 * Class<?>[] types = {String.<jk>class</jk>, Integer.<jk>class</jk>, Boolean.<jk>class</jk>}; 324 * Object[] args = {<js>"hello"</js>}; 325 * Object[] result = getMatchingArgs(types, args); 326 * <jc>// Returns: ["hello", null, null]</jc> 327 * 328 * <jc>// Handles primitive types and their wrappers</jc> 329 * Class<?>[] types = {<jk>int</jk>.<jk>class</jk>, String.<jk>class</jk>}; 330 * Object[] args = {<js>"hello"</js>, 42}; <jc>// Integer object matches int.class</jc> 331 * Object[] result = getMatchingArgs(types, args); 332 * <jc>// Returns: [42, "hello"]</jc> 333 * 334 * <jc>// Respects type hierarchy - subclasses match parent types</jc> 335 * Class<?>[] types = {Number.<jk>class</jk>, String.<jk>class</jk>}; 336 * Object[] args = {<js>"hello"</js>, 42}; <jc>// Integer extends Number</jc> 337 * Object[] result = getMatchingArgs(types, args); 338 * <jc>// Returns: [42, "hello"]</jc> 339 * </p> 340 * 341 * <p> 342 * This method is used internally by {@link ClassInfo}, {@link MethodInfo}, and {@link ConstructorInfo} 343 * to provide flexible parameter matching during reflective invocation. 344 * 345 * @param paramTypes The parameter types to match against. Must not be <jk>null</jk>. 346 * @param args The arguments to match to the parameter types. Can be empty or contain <jk>null</jk> values. 347 * @return 348 * An array of arguments matched to the parameter types. The returned array will always have 349 * the same length as {@code paramTypes}. Returns the original {@code args} array if it already 350 * matches (fast path optimization). 351 */ 352 public static Object[] getMatchingArgs(Class<?>[] paramTypes, Object...args) { 353 reqnn("paramTypes", paramTypes); 354 reqnn("args", args); 355 var needsShuffle = paramTypes.length != args.length; 356 if (! needsShuffle) { 357 for (var i = 0; i < paramTypes.length; i++) { 358 if (! paramTypes[i].isInstance(args[i])) 359 needsShuffle = true; 360 } 361 } 362 if (! needsShuffle) 363 return args; 364 var params = new Object[paramTypes.length]; 365 for (var i = 0; i < paramTypes.length; i++) { 366 var pt = info(paramTypes[i]).getWrapperIfPrimitive(); 367 for (var arg : args) { 368 if (isNotNull(arg) && pt.isAssignableFrom(arg.getClass())) { 369 params[i] = arg; 370 break; 371 } 372 } 373 } 374 return params; 375 } 376 377 /** 378 * Attempts to unwrap a proxy object and return the underlying "real" class. 379 * 380 * <p> 381 * This method handles several common proxy types used in Java applications: 382 * <ul> 383 * <li><b>JDK Dynamic Proxies</b> - Created via {@link Proxy#newProxyInstance} 384 * <li><b>CGLIB Proxies</b> - Created by Spring's CGLIB enhancer (class name contains <js>"$$EnhancerBySpringCGLIB$$"</js>) 385 * <li><b>Javassist Proxies</b> - Created by Javassist proxy factory (class name contains <js>"_$$_javassist"</js> or <js>"_$$_jvst"</js>) 386 * <li><b>ByteBuddy Proxies</b> - Created by ByteBuddy (class name contains <js>"$ByteBuddy$"</js>) 387 * <li><b>Hibernate lazy proxies</b> - Class name contains <js>"$HibernateProxy$"</js> 388 * </ul> 389 * 390 * <h5 class='section'>Example:</h5> 391 * <p class='bjava'> 392 * <jc>// Get the real class behind a Spring proxy</jc> 393 * <ja>@Service</ja> 394 * <jk>class</jk> MyService {} 395 * 396 * MyService proxy = applicationContext.getBean(MyService.<jk>class</jk>); 397 * Class<?> realClass = ClassUtils.<jsm>getProxyFor</jsm>(proxy); 398 * <jc>// Returns MyService.class instead of the CGLIB proxy class</jc> 399 * </p> 400 * 401 * <h5 class='section'>Notes:</h5> 402 * <ul class='spaced-list'> 403 * <li>For JDK dynamic proxies, returns the first interface implemented by the proxy 404 * <li>For CGLIB/Javassist/ByteBuddy/Hibernate proxies, returns the superclass 405 * <li>For Spring CGLIB proxies with a <c>getTargetClass()</c> method, invokes that method for more accurate results 406 * <li>Returns <jk>null</jk> if the object is not a recognized proxy type 407 * </ul> 408 * 409 * @param o The object to unwrap. Can be <jk>null</jk>. 410 * @return 411 * The underlying class, or <jk>null</jk> if: 412 * <ul> 413 * <li>The object is <jk>null</jk> 414 * <li>The object is not a recognized proxy type 415 * <li>The proxy cannot be unwrapped 416 * </ul> 417 */ 418 public static Class<?> getProxyFor(Object o) { 419 if (o == null) 420 return null; 421 422 var c = o.getClass(); 423 var r = PROXY_FOR.get(c); 424 425 if (r == NOT_PROXY) 426 return null; 427 428 if (r == CGLIB_PROXY) { 429 // CGLIB Proxy: Spring's CGLIB enhancer creates subclasses 430 // Pattern: com.example.MyClass$$EnhancerBySpringCGLIB$$abc123 431 // Try to invoke getTargetClass() if available (Spring specific) 432 var v = Holder.<Class<?>>empty(); 433 info(c).getPublicMethods().stream().filter(m -> m.hasName("getTargetClass") && m.getParameterCount() == 0 && m.hasReturnType(Class.class)).forEach(m -> safe(() -> v.set(m.invoke(o)))); // HTT - Requires bytecode manipulation to create classes with $$EnhancerBySpringCGLIB$$ in name 434 return v.isPresent() ? v.get() : c.getSuperclass(); // HTT - Requires bytecode manipulation to create classes with $$EnhancerBySpringCGLIB$$ in name 435 } 436 437 return (Class<?>)r; 438 } 439 440 private static final Object NOT_PROXY = new Object(); 441 private static final Object CGLIB_PROXY = new Object(); 442 443 /** 444 * Per-class proxy classification used by {@link #getProxyFor(Object)}. 445 * 446 * <p> 447 * The value is {@link #NOT_PROXY}, {@link #CGLIB_PROXY} (which needs the instance to resolve the target class), 448 * or the resolved underlying {@link Class}. 449 */ 450 private static final ClassValue<Object> PROXY_FOR = new ClassValue<>() { 451 @Override 452 protected Object computeValue(Class<?> c) { 453 var s = c.getName(); 454 455 // Quick exit: Most classes aren't proxies, and proxies typically have '$' in their name 456 if (s.indexOf('$') == -1) 457 return NOT_PROXY; 458 459 // JDK Dynamic Proxy: Created via Proxy.newProxyInstance() 460 // These implement interfaces and are instances of Proxy 461 if (Proxy.isProxyClass(c)) { 462 var interfaces = c.getInterfaces(); 463 return interfaces.length > 0 ? interfaces[0] : NOT_PROXY; 464 } 465 466 if (s.contains("$$EnhancerBySpringCGLIB$$")) 467 return CGLIB_PROXY; 468 469 // Javassist / ByteBuddy / Hibernate lazy association proxies: subclasses of the user type 470 if (s.contains("_$$_javassist") || s.contains("_$$_jvst") || s.contains("$ByteBuddy$") || s.contains("$HibernateProxy$")) { 471 var sc = c.getSuperclass(); // HTT - Requires bytecode manipulation to create classes with _$$_javassist, _$$_jvst, $ByteBuddy$, or $HibernateProxy$ in name 472 return sc == null ? NOT_PROXY : sc; 473 } 474 475 // Not a recognized proxy type 476 return NOT_PROXY; 477 } 478 }; 479 480 /** 481 * Returns <jk>false</jk> if the specific class is <jk>null</jk> or <c><jk>void</jk>.<jk>class</jk></c> or {@link Void} or has the simple name <js>"Void</js>. 482 * 483 * @param c The class to check. 484 * @return <jk>false</jk> if the specific class is <jk>null</jk> or <c><jk>void</jk>.<jk>class</jk></c> or {@link Void} or has the simple name <js>"Void</js>. 485 */ 486 public static boolean isNotVoid(Class c) { 487 return ! isVoid(c); 488 } 489 490 /** 491 * Returns <jk>true</jk> if the specific class is <jk>null</jk> or <c><jk>void</jk>.<jk>class</jk></c> or {@link Void} or has the simple name <js>"Void</js>. 492 * 493 * @param c The class to check. 494 * @return <jk>true</jk> if the specific class is <jk>null</jk> or <c><jk>void</jk>.<jk>class</jk></c> or {@link Void} or has the simple name <js>"Void</js>. 495 */ 496 public static boolean isVoid(Class c) { 497 return c == null || c == void.class || c == Void.class || classNameSimple(c).equalsIgnoreCase("void"); 498 } 499 500 /** 501 * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. 502 * 503 * @param x The constructor. Must not be <jk>null</jk> (a <jk>null</jk> argument throws {@link IllegalArgumentException}). 504 * @return <jk>true</jk> if call was successful. 505 */ 506 public static boolean setAccessible(Constructor<?> x) { 507 reqnn("x", x); 508 return safeOpt(() -> { 509 x.setAccessible(true); 510 return true; 511 }).orElse(false); 512 } 513 514 /** 515 * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. 516 * 517 * @param x The field. Must not be <jk>null</jk> (a <jk>null</jk> argument throws {@link IllegalArgumentException}). 518 * @return <jk>true</jk> if call was successful. 519 */ 520 public static boolean setAccessible(Field x) { 521 reqnn("x", x); 522 return safeOpt(() -> { 523 x.setAccessible(true); 524 return true; 525 }).orElse(false); 526 } 527 528 /** 529 * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. 530 * 531 * @param x The method. Must not be <jk>null</jk> (a <jk>null</jk> argument throws {@link IllegalArgumentException}). 532 * @return <jk>true</jk> if call was successful. 533 */ 534 public static boolean setAccessible(Method x) { 535 reqnn("x", x); 536 return safeOpt(() -> { 537 x.setAccessible(true); 538 return true; 539 }).orElse(false); 540 } 541 542 /** 543 * Returns the specified type as a <c>Class</c>. 544 * 545 * <p> 546 * If it's already a <c>Class</c>, it just does a cast. 547 * <br>If it's a <c>ParameterizedType</c>, it returns the raw type. 548 * 549 * @param t The type to convert. Can be <jk>null</jk> (returns <jk>null</jk>). 550 * @return The type converted to a <c>Class</c>, or <jk>null</jk> if it could not be converted. 551 */ 552 public static Class<?> toClass(Type t) { 553 if (t instanceof Class<?> t2) 554 return t2; 555 if (t instanceof ParameterizedType t2) { 556 // The raw type should always be a class (right?) 557 return (Class<?>)t2.getRawType(); 558 } 559 return null; 560 } 561 562 private static boolean canAddTo(Class<?> c) { 563 var b = modifiableCollectionTypes.get(c); 564 if (b == null) { 565 var name = c.getName(); 566 b = (! name.contains("Immutable") && ! name.contains("Unmodifiable") && ! name.contains("Arrays$ArrayList")); 567 modifiableCollectionTypes.put(c, b); 568 } 569 return b; 570 } 571 572 /** 573 * Casts the object to the specified type, returning null if not an instance. 574 * 575 * @param <T> The type to cast to. 576 * @param c The type to cast to. Must not be <jk>null</jk> when the object is non-<jk>null</jk> (unguarded {@link NullPointerException} otherwise). 577 * @param o The object to cast. Can be <jk>null</jk> (returns <jk>null</jk>). 578 * @return The cast object, or null if the object was null or not an instance of c. 579 */ 580 @SuppressWarnings({ 581 "java:S1168" // Intentional null return. 582 }) 583 public static <T> T castTo(Class<T> c, Object o) { 584 return o != null && c.isInstance(o) ? c.cast(o) : null; 585 } 586 587 /** Returns true if the object is a non-null array. */ 588 public static boolean isArray(Object o) { return o != null && o.getClass().isArray(); } 589}