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&lt;&gt;());        <jc>// true</jc>
071    *    canAddTo(<jk>new</jk> LinkedList&lt;&gt;());       <jc>// true</jc>
072    *    canAddTo(<jk>new</jk> HashSet&lt;&gt;());          <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&lt;&gt;());          <jc>// true</jc>
110    *    canPutTo(<jk>new</jk> LinkedHashMap&lt;&gt;());    <jc>// true</jc>
111    *    canPutTo(<jk>new</jk> TreeMap&lt;&gt;());          <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&lt;&gt;());                <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&lt;&gt;());          <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&lt;&gt;());                   <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&lt;?&gt;[] 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&lt;?&gt;[] 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&lt;?&gt;[] 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&lt;?&gt;[] 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&lt;?&gt;[] 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&lt;?&gt;[] 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&lt;?&gt; 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}