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 *      https://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.commons.lang3;
018
019import java.lang.reflect.Method;
020import java.lang.reflect.Modifier;
021import java.util.ArrayDeque;
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.Comparator;
025import java.util.Deque;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.Iterator;
029import java.util.LinkedHashSet;
030import java.util.List;
031import java.util.Map;
032import java.util.Objects;
033import java.util.Set;
034import java.util.concurrent.atomic.AtomicReference;
035import java.util.regex.Pattern;
036import java.util.stream.Collectors;
037
038/**
039 * Operates on classes without using reflection.
040 *
041 * <p>
042 * This class handles invalid {@code null} inputs as best it can. Each method documents its behavior in more detail.
043 * </p>
044 *
045 * <p>
046 * The notion of a {@code canonical name} includes the human-readable name for the type, for example {@code int[]}. The
047 * non-canonical method variants work with the JVM names, such as {@code [I}.
048 * </p>
049 *
050 * @since 2.0
051 */
052public class ClassUtils {
053
054    /**
055     * Enumerates inclusivity literals for {@link #hierarchy(Class, Interfaces)}.
056     *
057     * @since 3.2
058     */
059    public enum Interfaces {
060
061        /** Includes interfaces. */
062        INCLUDE,
063
064        /** Excludes interfaces. */
065        EXCLUDE
066    }
067
068    /**
069     * The JLS-specified maximum class name length {@value}.
070     *
071     * @see Class#forName(String, boolean, ClassLoader)
072     * @see <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.4.1">JVM: Array dimension limits in JVM Specification CONSTANT_Class_info</a>
073     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7">JLS: Fully Qualified Names and Canonical Names</a>
074     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1">JLS: The Form of a Binary</a>
075     */
076    private static final int MAX_CLASS_NAME_LENGTH = 65535;
077
078    /**
079     * The JVM-specified {@code CONSTANT_Class_info} structure defines an array type descriptor is valid only if it represents {@value} or fewer dimensions.
080     *
081     * @see Class#forName(String, boolean, ClassLoader)
082     * @see <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.4.1">JVM: Array dimension limits in JVM Specification CONSTANT_Class_info</a>
083     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7">JLS: Fully Qualified Names and Canonical Names</a>
084     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1">JLS: The Form of a Binary</a>
085     */
086    private static final int MAX_JVM_ARRAY_DIMENSION = 255;
087
088    /**
089     * The maximum number of array dimensions.
090     */
091    private static final int MAX_DIMENSIONS = 255;
092
093    private static final Pattern ARRAY_TAIL_PATTERN = Pattern.compile("(?:\\[\\])+");
094
095    private static final Comparator<Class<?>> COMPARATOR = (o1, o2) -> Objects.compare(getName(o1), getName(o2), String::compareTo);
096
097    /**
098     * The package separator character: {@code '&#x2e;' == {@value}}.
099     */
100    public static final char PACKAGE_SEPARATOR_CHAR = '.';
101
102    /**
103     * The package separator String: {@code "&#x2e;"}.
104     */
105    public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);
106
107    /**
108     * The inner class separator character: {@code '$' == {@value}}.
109     */
110    public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
111
112    /**
113     * The inner class separator String: {@code "$"}.
114     */
115    public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);
116
117    /**
118     * Maps names of primitives to their corresponding primitive {@link Class}es.
119     */
120    private static final Map<String, Class<?>> NAME_PRIMITIVE_MAP = new HashMap<>();
121
122    static {
123        NAME_PRIMITIVE_MAP.put(Boolean.TYPE.getName(), Boolean.TYPE);
124        NAME_PRIMITIVE_MAP.put(Byte.TYPE.getName(), Byte.TYPE);
125        NAME_PRIMITIVE_MAP.put(Character.TYPE.getName(), Character.TYPE);
126        NAME_PRIMITIVE_MAP.put(Double.TYPE.getName(), Double.TYPE);
127        NAME_PRIMITIVE_MAP.put(Float.TYPE.getName(), Float.TYPE);
128        NAME_PRIMITIVE_MAP.put(Integer.TYPE.getName(), Integer.TYPE);
129        NAME_PRIMITIVE_MAP.put(Long.TYPE.getName(), Long.TYPE);
130        NAME_PRIMITIVE_MAP.put(Short.TYPE.getName(), Short.TYPE);
131        NAME_PRIMITIVE_MAP.put(Void.TYPE.getName(), Void.TYPE);
132    }
133
134    /**
135     * Maps primitive {@link Class}es to their corresponding wrapper {@link Class}.
136     */
137    private static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP = new HashMap<>();
138
139    static {
140        PRIMITIVE_WRAPPER_MAP.put(Boolean.TYPE, Boolean.class);
141        PRIMITIVE_WRAPPER_MAP.put(Byte.TYPE, Byte.class);
142        PRIMITIVE_WRAPPER_MAP.put(Character.TYPE, Character.class);
143        PRIMITIVE_WRAPPER_MAP.put(Short.TYPE, Short.class);
144        PRIMITIVE_WRAPPER_MAP.put(Integer.TYPE, Integer.class);
145        PRIMITIVE_WRAPPER_MAP.put(Long.TYPE, Long.class);
146        PRIMITIVE_WRAPPER_MAP.put(Double.TYPE, Double.class);
147        PRIMITIVE_WRAPPER_MAP.put(Float.TYPE, Float.class);
148        PRIMITIVE_WRAPPER_MAP.put(Void.TYPE, Void.TYPE);
149    }
150
151    /**
152     * Maps wrapper {@link Class}es to their corresponding primitive types.
153     */
154    private static final Map<Class<?>, Class<?>> WRAPPER_PRIMITIVE_MAP = new HashMap<>();
155
156    static {
157        PRIMITIVE_WRAPPER_MAP.forEach((primitiveClass, wrapperClass) -> {
158            if (!primitiveClass.equals(wrapperClass)) {
159                WRAPPER_PRIMITIVE_MAP.put(wrapperClass, primitiveClass);
160            }
161        });
162    }
163
164    /**
165     * Maps a primitive class name to its corresponding abbreviation used in array class names.
166     */
167    private static final Map<String, String> ABBREVIATION_MAP;
168
169    /**
170     * Maps an abbreviation used in array class names to corresponding primitive class name.
171     */
172    private static final Map<String, String> REVERSE_ABBREVIATION_MAP;
173
174    /** Feed abbreviation maps. */
175    static {
176        final Map<String, String> map = new HashMap<>();
177        map.put(Integer.TYPE.getName(), "I");
178        map.put(Boolean.TYPE.getName(), "Z");
179        map.put(Float.TYPE.getName(), "F");
180        map.put(Long.TYPE.getName(), "J");
181        map.put(Short.TYPE.getName(), "S");
182        map.put(Byte.TYPE.getName(), "B");
183        map.put(Double.TYPE.getName(), "D");
184        map.put(Character.TYPE.getName(), "C");
185        ABBREVIATION_MAP = Collections.unmodifiableMap(map);
186        REVERSE_ABBREVIATION_MAP = Collections.unmodifiableMap(map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)));
187    }
188
189    /**
190     * Gets the class comparator, comparing by class name.
191     *
192     * @return The class comparator.
193     * @since 3.13.0
194     */
195    public static Comparator<Class<?>> comparator() {
196        return COMPARATOR;
197    }
198
199    /**
200     * Given a {@link List} of {@link Class} objects, this method converts them into class names.
201     *
202     * <p>
203     * A new {@link List} is returned. {@code null} objects will be copied into the returned list as {@code null}.
204     * </p>
205     *
206     * @param classes The classes to change.
207     * @return A {@link List} of class names corresponding to the Class objects, {@code null} if null input.
208     * @throws ClassCastException Thrown if {@code classes} contains a non-{@link Class} entry.
209     */
210    public static List<String> convertClassesToClassNames(final List<Class<?>> classes) {
211        return classes == null ? null : classes.stream().map(e -> getName(e, null)).collect(Collectors.toList());
212    }
213
214    /**
215     * Given a {@link List} of class names, this method converts them into classes.
216     *
217     * <p>
218     * A new {@link List} is returned. If the class name cannot be found, {@code null} is stored in the {@link List}. If the
219     * class name in the {@link List} is {@code null}, {@code null} is stored in the output {@link List}.
220     * </p>
221     *
222     * @param classNames The classNames to change.
223     * @return A {@link List} of Class objects corresponding to the class names, {@code null} if null input.
224     * @throws ClassCastException Thrown if classNames contains a non String entry.
225     */
226    public static List<Class<?>> convertClassNamesToClasses(final List<String> classNames) {
227        if (classNames == null) {
228            return null;
229        }
230        final List<Class<?>> classes = new ArrayList<>(classNames.size());
231        classNames.forEach(className -> {
232            try {
233                classes.add(Class.forName(className));
234            } catch (final Exception ex) {
235                classes.add(null);
236            }
237        });
238        return classes;
239    }
240
241    /**
242     * Gets the abbreviated name of a {@link Class}.
243     *
244     * @param cls The class to get the abbreviated name for, may be {@code null}.
245     * @param lengthHint The desired length of the abbreviated name.
246     * @return The abbreviated name or an empty string.
247     * @throws IllegalArgumentException Thrown if len &lt;= 0.
248     * @see #getAbbreviatedName(String, int)
249     * @since 3.4
250     */
251    public static String getAbbreviatedName(final Class<?> cls, final int lengthHint) {
252        if (cls == null) {
253            return StringUtils.EMPTY;
254        }
255        return getAbbreviatedName(cls.getName(), lengthHint);
256    }
257
258    /**
259     * Gets the abbreviated class name from a {@link String}.
260     *
261     * <p>
262     * The string passed in is assumed to be a class name - it is not checked.
263     * </p>
264     *
265     * <p>
266     * The abbreviation algorithm will shorten the class name, usually without significant loss of meaning.
267     * </p>
268     *
269     * <p>
270     * The abbreviated class name will always include the complete package hierarchy. If enough space is available,
271     * rightmost sub-packages will be displayed in full length. The abbreviated package names will be shortened to a single
272     * character.
273     * </p>
274     * <p>
275     * Only package names are shortened, the class simple name remains untouched. (See examples.)
276     * </p>
277     * <p>
278     * The result will be longer than the desired length only if all the package names shortened to a single character plus
279     * the class simple name with the separating dots together are longer than the desired length. In other words, when the
280     * class name cannot be shortened to the desired length.
281     * </p>
282     * <p>
283     * If the class name can be shortened then the final length will be at most {@code lengthHint} characters.
284     * </p>
285     * <p>
286     * If the {@code lengthHint} is zero or negative then the method throws exception. If you want to achieve the shortest
287     * possible version then use {@code 1} as a {@code lengthHint}.
288     * </p>
289     *
290     * <table>
291     * <caption>Examples</caption>
292     * <tr>
293     * <td>className</td>
294     * <td>len</td>
295     * <td>return</td>
296     * </tr>
297     * <tr>
298     * <td>null</td>
299     * <td>1</td>
300     * <td>""</td>
301     * </tr>
302     * <tr>
303     * <td>"java.lang.String"</td>
304     * <td>5</td>
305     * <td>"j.l.String"</td>
306     * </tr>
307     * <tr>
308     * <td>"java.lang.String"</td>
309     * <td>15</td>
310     * <td>"j.lang.String"</td>
311     * </tr>
312     * <tr>
313     * <td>"java.lang.String"</td>
314     * <td>30</td>
315     * <td>"java.lang.String"</td>
316     * </tr>
317     * <tr>
318     * <td>"org.apache.commons.lang3.ClassUtils"</td>
319     * <td>18</td>
320     * <td>"o.a.c.l.ClassUtils"</td>
321     * </tr>
322     * </table>
323     *
324     * @param className The className to get the abbreviated name for, may be {@code null}.
325     * @param lengthHint The desired length of the abbreviated name.
326     * @return The abbreviated name or an empty string if the specified class name is {@code null} or empty string. The
327     *         abbreviated name may be longer than the desired length if it cannot be abbreviated to the desired length.
328     * @throws IllegalArgumentException Thrown if {@code len <= 0}.
329     * @since 3.4
330     */
331    public static String getAbbreviatedName(final String className, final int lengthHint) {
332        if (lengthHint <= 0) {
333            throw new IllegalArgumentException("len must be > 0");
334        }
335        if (className == null) {
336            return StringUtils.EMPTY;
337        }
338        if (className.length() <= lengthHint) {
339            return className;
340        }
341        // Keep the array encoding (for example "[[L" ... ";") intact and only abbreviate the element class name
342        int dims = 0;
343        while (dims < className.length() && className.charAt(dims) == '[') {
344            dims++;
345        }
346        if (dims > 0 && className.length() > dims + 2 && className.charAt(dims) == 'L' && className.charAt(className.length() - 1) == ';') {
347            final String elementName = className.substring(dims + 1, className.length() - 1);
348            return className.substring(0, dims + 1) + getAbbreviatedName(elementName, Math.max(1, lengthHint - dims - 2)) + ';';
349        }
350        final char[] abbreviated = className.toCharArray();
351        int target = 0;
352        int source = 0;
353        while (source < abbreviated.length) {
354            // copy the next part
355            int runAheadTarget = target;
356            while (source < abbreviated.length && abbreviated[source] != '.') {
357                abbreviated[runAheadTarget++] = abbreviated[source++];
358            }
359
360            ++target;
361            if (useFull(runAheadTarget, source, abbreviated.length, lengthHint) || target > runAheadTarget) {
362                target = runAheadTarget;
363            }
364
365            // copy the '.' unless it was the last part
366            if (source < abbreviated.length) {
367                abbreviated[target++] = abbreviated[source++];
368            }
369        }
370        return new String(abbreviated, 0, target);
371    }
372
373    /**
374     * Gets a {@link List} of all interfaces implemented by the given class and its superclasses.
375     *
376     * <p>
377     * The order is determined by looking through each interface in turn as declared in the source file and following its
378     * hierarchy up. Then each superclass is considered in the same way. Later duplicates are ignored, so the order is
379     * maintained.
380     * </p>
381     *
382     * @param cls The class to look up, may be {@code null}.
383     * @return The {@link List} of interfaces in order, {@code null} if null input.
384     */
385    public static List<Class<?>> getAllInterfaces(final Class<?> cls) {
386        if (cls == null) {
387            return null;
388        }
389        final LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<>();
390        getAllInterfaces(cls, interfacesFound);
391        return new ArrayList<>(interfacesFound);
392    }
393
394    /**
395     * Gets the interfaces for the specified class.
396     *
397     * @param cls The class to look up, may be {@code null}.
398     * @param interfacesFound The {@link Set} of interfaces for the class.
399     */
400    private static void getAllInterfaces(Class<?> cls, final Set<Class<?>> interfacesFound) {
401        while (cls != null) {
402            for (final Class<?> i : cls.getInterfaces()) {
403                if (interfacesFound.add(i)) {
404                    getAllInterfaces(i, interfacesFound);
405                }
406            }
407            cls = cls.getSuperclass();
408        }
409    }
410
411    /**
412     * Gets a {@link List} of superclasses for the given class.
413     *
414     * <ol>
415     * <li>The first entry is the superclass of the given class.</li>
416     * <li>The last entry is {@link Object}'s class.</li>
417     * </ol>
418     *
419     * @param cls The class to look up, may be {@code null}.
420     * @return The {@link List} of superclasses in order going up from this one {@code null} if null input.
421     */
422    public static List<Class<?>> getAllSuperclasses(final Class<?> cls) {
423        if (cls == null) {
424            return null;
425        }
426        final List<Class<?>> classes = new ArrayList<>();
427        Class<?> superclass = cls.getSuperclass();
428        while (superclass != null) {
429            classes.add(superclass);
430            superclass = superclass.getSuperclass();
431        }
432        return classes;
433    }
434
435    /**
436     * Gets the canonical class name for a {@link Class}.
437     *
438     * @param cls The class for which to get the canonical class name; may be null.
439     * @return The canonical name of the class, or the empty String.
440     * @since 3.7
441     * @see Class#getCanonicalName()
442     */
443    public static String getCanonicalName(final Class<?> cls) {
444        return getCanonicalName(cls, StringUtils.EMPTY);
445    }
446
447    /**
448     * Gets the canonical name for a {@link Class}.
449     *
450     * @param cls The class for which to get the canonical class name; may be null.
451     * @param valueIfNull The return value if null.
452     * @return The canonical name of the class, or {@code valueIfNull}.
453     * @since 3.7
454     * @see Class#getCanonicalName()
455     */
456    public static String getCanonicalName(final Class<?> cls, final String valueIfNull) {
457        if (cls == null) {
458            return valueIfNull;
459        }
460        final String canonicalName = cls.getCanonicalName();
461        return canonicalName == null ? valueIfNull : canonicalName;
462    }
463
464    /**
465     * Gets the canonical name for an {@link Object}.
466     *
467     * @param object The object for which to get the canonical class name; may be null.
468     * @return The canonical name of the object, or the empty String.
469     * @since 3.7
470     * @see Class#getCanonicalName()
471     */
472    public static String getCanonicalName(final Object object) {
473        return getCanonicalName(object, StringUtils.EMPTY);
474    }
475
476    /**
477     * Gets the canonical name for an {@link Object}.
478     *
479     * @param object The object for which to get the canonical class name; may be null.
480     * @param valueIfNull The return value if null.
481     * @return The canonical name of the object or {@code valueIfNull}.
482     * @since 3.7
483     * @see Class#getCanonicalName()
484     */
485    public static String getCanonicalName(final Object object, final String valueIfNull) {
486        if (object == null) {
487            return valueIfNull;
488        }
489        final String canonicalName = object.getClass().getCanonicalName();
490        return canonicalName == null ? valueIfNull : canonicalName;
491    }
492
493    /**
494     * Gets the canonical form of the given class name. Non-array class names are returned unchanged.
495     *
496     * <p>
497     * The method does not change the {@code $} separators if the class is an inner class.
498     * </p>
499     *
500     * <p>
501     * Example:
502     * <ul>
503     * <li>{@code getCanonicalName("[I") = "int[]"}</li>
504     * <li>{@code getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"}</li>
505     * <li>{@code getCanonicalName("java.lang.String") = "java.lang.String"}</li>
506     * </ul>
507     * </p>
508     *
509     * @param name The name of class.
510     * @return canonical form of class name.
511     * @throws IllegalArgumentException Thrown if the class name is invalid.
512     */
513    private static String getCanonicalName(final String name) {
514        String className = StringUtils.deleteWhitespace(name);
515        if (className == null) {
516            return null;
517        }
518        int dim = 0;
519        final int len = className.length();
520        while (dim < len && className.charAt(dim) == '[') {
521            dim++;
522            if (dim > MAX_DIMENSIONS) {
523                throw new IllegalArgumentException(String.format("Maximum array dimension %d exceeded", MAX_DIMENSIONS));
524            }
525        }
526        if (dim >= len) {
527            throw new IllegalArgumentException(String.format("Invalid class name %s", name));
528        }
529        if (dim < 1) {
530            return className;
531        }
532        className = className.substring(dim);
533        if (className.startsWith("L")) {
534            if (!className.endsWith(";") || className.length() < 3) {
535                throw new IllegalArgumentException(String.format("Invalid class name %s", name));
536            }
537            className = className.substring(1, className.length() - 1);
538        } else if (className.length() == 1) {
539            final String primitive = REVERSE_ABBREVIATION_MAP.get(className.substring(0, 1));
540            if (primitive == null) {
541                throw new IllegalArgumentException(String.format("Invalid class name %s", name));
542            }
543            className = primitive;
544        } else {
545            throw new IllegalArgumentException(String.format("Invalid class name %s", name));
546        }
547        final StringBuilder canonicalClassNameBuffer = new StringBuilder(className.length() + dim * 2);
548        canonicalClassNameBuffer.append(className);
549        for (int i = 0; i < dim; i++) {
550            canonicalClassNameBuffer.append("[]");
551        }
552        return canonicalClassNameBuffer.toString();
553    }
554
555    /**
556     * Gets the (initialized) class represented by {@code className} using the {@code classLoader}. This implementation
557     * supports the syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
558     * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
559     * <p>
560     * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not
561     * been collapsed.
562     * </p>
563     * <p>
564     * <strong>Security note:</strong> because all whitespace is deleted before the class is resolved (and a failing {@code '.'} may be retried as {@code '$'}
565     * for inner classes), many distinct input strings resolve to the same class, while {@link Class#forName(String)} performs no such normalization. Validating
566     * an untrusted class name by string comparison <em>before</em> calling this method is therefore unsound: for example, {@code " java.lang.Runtime"} fails a
567     * naive {@code startsWith("java.")} denylist check on the raw string, yet loads {@code java.lang.Runtime}. Validate the class name <em>after</em>
568     * normalization, validate the resolved {@link Class} object itself, or use {@link #getClassStrict(ClassLoader, String, boolean)} which performs no
569     * whitespace normalization.
570     * </p>
571     *
572     * @param classLoader The class loader to use to load the class.
573     * @param className The class name.
574     * @return The class represented by {@code className} using the {@code classLoader}.
575     * @throws NullPointerException Thrown if the className is null.
576     * @throws ClassNotFoundException Thrown if the class is not found.
577     * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
578     * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
579     * @see Class#forName(String, boolean, ClassLoader)
580     * @see <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.4.1">JVM: Array dimension limits in JVM Specification CONSTANT_Class_info</a>
581     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7">JLS: Fully Qualified Names and Canonical Names</a>
582     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1">JLS: The Form of a Binary</a>
583     */
584    public static Class<?> getClass(final ClassLoader classLoader, final String className) throws ClassNotFoundException {
585        return getClass(classLoader, className, true);
586    }
587
588    /**
589     * Gets the class represented by {@code className} using the {@code classLoader}. This implementation supports the
590     * syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", and
591     * "{@code [Ljava.util.Map$Entry;}".
592     * <p>
593     * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not
594     * been collapsed.
595     * </p>
596     * <p>
597     * <strong>Security note:</strong> because all whitespace is deleted before the class is resolved (and a failing {@code '.'} may be retried as {@code '$'}
598     * for inner classes), many distinct input strings resolve to the same class, while {@link Class#forName(String)} performs no such normalization. Validating
599     * an untrusted class name by string comparison <em>before</em> calling this method is therefore unsound: for example, {@code " java.lang.Runtime"} fails a
600     * naive {@code startsWith("java.")} denylist check on the raw string, yet loads {@code java.lang.Runtime}. Validate the class name <em>after</em>
601     * normalization, validate the resolved {@link Class} object itself, or use {@link #getClassStrict(ClassLoader, String, boolean)} which performs no
602     * whitespace normalization.
603     * </p>
604     *
605     * @param classLoader The class loader to use to load the class.
606     * @param className The class name.
607     * @param initialize whether the class must be initialized.
608     * @return The class represented by {@code className} using the {@code classLoader}.
609     * @throws NullPointerException Thrown if the className is null.
610     * @throws ClassNotFoundException Thrown if the class is not found.
611     * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
612     * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
613     * @see Class#forName(String, boolean, ClassLoader)
614     * @see <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.4.1">JVM: Array dimension limits in JVM Specification CONSTANT_Class_info</a>
615     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7">JLS: Fully Qualified Names and Canonical Names</a>
616     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1">JLS: The Form of a Binary</a>
617     */
618    public static Class<?> getClass(final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException {
619        return getClass(classLoader, className, initialize, true);
620    }
621
622    /**
623     * Gets a class using the shared implementation of {@link #getClass(ClassLoader, String, boolean)} and
624     * {@link #getClassStrict(ClassLoader, String, boolean)}.
625     *
626     * @param classLoader The class loader to use to load the class.
627     * @param className The class name.
628     * @param initialize whether the class must be initialized.
629     * @param normalizeWhitespace whether to delete all whitespace from the class name before resolving it.
630     * @return The class represented by {@code className} using the {@code classLoader}.
631     * @throws NullPointerException Thrown if the className is null.
632     * @throws ClassNotFoundException Thrown if the class is not found.
633     */
634    private static Class<?> getClass(final ClassLoader classLoader, final String className, final boolean initialize, final boolean normalizeWhitespace)
635            throws ClassNotFoundException {
636        // This method was re-written to avoid recursion and stack overflows found by fuzz testing.
637        String next = className;
638        int lastDotIndex = -1;
639        do {
640            try {
641                final Class<?> clazz = getPrimitiveClass(next);
642                return clazz != null ? clazz : Class.forName(normalizeWhitespace ? toCleanName(next) : toEncodedName(next), initialize, classLoader);
643            } catch (final ClassNotFoundException ex) {
644                lastDotIndex = next.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
645                if (lastDotIndex != -1) {
646                    next = next.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR_CHAR + next.substring(lastDotIndex + 1);
647                }
648            }
649        } while (lastDotIndex != -1);
650        throw new ClassNotFoundException(className);
651    }
652
653    /**
654     * Gets the (initialized) class represented by {@code className} using the current thread's context class loader.
655     * This implementation supports the syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
656     * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
657     * <p>
658     * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not
659     * been collapsed.
660     * </p>
661     * <p>
662     * <strong>Security note:</strong> because all whitespace is deleted before the class is resolved (and a failing {@code '.'} may be retried as {@code '$'}
663     * for inner classes), many distinct input strings resolve to the same class, while {@link Class#forName(String)} performs no such normalization. Validating
664     * an untrusted class name by string comparison <em>before</em> calling this method is therefore unsound: for example, {@code " java.lang.Runtime"} fails a
665     * naive {@code startsWith("java.")} denylist check on the raw string, yet loads {@code java.lang.Runtime}. Validate the class name <em>after</em>
666     * normalization, validate the resolved {@link Class} object itself, or use {@link #getClassStrict(ClassLoader, String, boolean)} which performs no
667     * whitespace normalization.
668     * </p>
669     *
670     * @param className The class name
671     * @return The class represented by {@code className} using the current thread's context class loader
672     * @throws NullPointerException Thrown if the className is null.
673     * @throws ClassNotFoundException Thrown if the class is not found.
674     * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
675     * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
676     * @see Class#forName(String, boolean, ClassLoader)
677     * @see <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.4.1">JVM: Array dimension limits in JVM Specification CONSTANT_Class_info</a>
678     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7">JLS: Fully Qualified Names and Canonical Names</a>
679     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1">JLS: The Form of a Binary</a>
680     */
681    public static Class<?> getClass(final String className) throws ClassNotFoundException {
682        return getClass(className, true);
683    }
684
685    /**
686     * Gets the class represented by {@code className} using the current thread's context class loader. This
687     * implementation supports the syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
688     * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
689     * <p>
690     * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not
691     * been collapsed.
692     * </p>
693     * <p>
694     * <strong>Security note:</strong> because all whitespace is deleted before the class is resolved (and a failing {@code '.'} may be retried as {@code '$'}
695     * for inner classes), many distinct input strings resolve to the same class, while {@link Class#forName(String)} performs no such normalization. Validating
696     * an untrusted class name by string comparison <em>before</em> calling this method is therefore unsound: for example, {@code " java.lang.Runtime"} fails a
697     * naive {@code startsWith("java.")} denylist check on the raw string, yet loads {@code java.lang.Runtime}. Validate the class name <em>after</em>
698     * normalization, validate the resolved {@link Class} object itself, or use {@link #getClassStrict(ClassLoader, String, boolean)} which performs no
699     * whitespace normalization.
700     * </p>
701     *
702     * @param className The class name.
703     * @param initialize whether the class must be initialized.
704     * @return The class represented by {@code className} using the current thread's context class loader.
705     * @throws NullPointerException Thrown if the className is null.
706     * @throws ClassNotFoundException Thrown if the class is not found.
707     * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
708     * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
709     * @see Class#forName(String, boolean, ClassLoader)
710     * @see <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.4.1">JVM: Array dimension limits in JVM Specification CONSTANT_Class_info</a>
711     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7">JLS: Fully Qualified Names and Canonical Names</a>
712     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1">JLS: The Form of a Binary</a>
713     */
714    public static Class<?> getClass(final String className, final boolean initialize) throws ClassNotFoundException {
715        final ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
716        final ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL;
717        return getClass(loader, className, initialize);
718    }
719
720    /**
721     * Gets the class represented by {@code className} using the {@code classLoader}, without normalizing the class name.
722     * <p>
723     * Unlike {@link #getClass(ClassLoader, String, boolean)}, this method does <em>not</em> delete whitespace from the class name: a name that differs from
724     * the intended binary name only by whitespace throws {@link ClassNotFoundException}, matching {@link Class#forName(String, boolean, ClassLoader)}. Use
725     * this variant when the class name may come from an untrusted source, so that host-side string validation of the raw name cannot be bypassed through
726     * whitespace the loader would otherwise silently remove.
727     * </p>
728     * <p>
729     * The syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}"
730     * are still supported: a failing {@code '.'} is retried as {@code '$'} to find inner classes, so more than one dotted spelling can still resolve to the
731     * same inner class. When validating untrusted names, prefer validating the resolved {@link Class} object.
732     * </p>
733     *
734     * @param classLoader The class loader to use to load the class.
735     * @param className The class name.
736     * @param initialize whether the class must be initialized.
737     * @return The class represented by {@code className} using the {@code classLoader}.
738     * @throws NullPointerException Thrown if the className is null.
739     * @throws ClassNotFoundException Thrown if the class is not found.
740     * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
741     * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
742     * @see Class#forName(String, boolean, ClassLoader)
743     * @see #getClass(ClassLoader, String, boolean)
744     * @since 3.21.0
745     */
746    public static Class<?> getClassStrict(final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException {
747        return getClass(classLoader, className, initialize, false);
748    }
749
750    /**
751     * Gets the (initialized) class represented by {@code className} using the current thread's context class loader, without normalizing the class name.
752     * <p>
753     * Unlike {@link #getClass(String)}, this method does <em>not</em> delete whitespace from the class name: a name that differs from the intended binary
754     * name only by whitespace throws {@link ClassNotFoundException}, matching {@link Class#forName(String)}. Use this variant when the class name may come
755     * from an untrusted source, so that host-side string validation of the raw name cannot be bypassed through whitespace the loader would otherwise silently
756     * remove.
757     * </p>
758     * <p>
759     * The syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}"
760     * are still supported: a failing {@code '.'} is retried as {@code '$'} to find inner classes, so more than one dotted spelling can still resolve to the
761     * same inner class. When validating untrusted names, prefer validating the resolved {@link Class} object.
762     * </p>
763     *
764     * @param className The class name.
765     * @return The class represented by {@code className} using the current thread's context class loader.
766     * @throws NullPointerException Thrown if the className is null.
767     * @throws ClassNotFoundException Thrown if the class is not found.
768     * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
769     * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
770     * @see Class#forName(String)
771     * @see #getClass(String)
772     * @since 3.21.0
773     */
774    public static Class<?> getClassStrict(final String className) throws ClassNotFoundException {
775        final ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
776        final ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL;
777        return getClassStrict(loader, className, true);
778    }
779
780    /**
781     * Gets the array component type using {@link Class#getComponentType()} with generics.
782     *
783     * @param <T> The array class type.
784     * @param cls A class or null.
785     * @return The array component type or null.
786     * @see Class#getComponentType()
787     * @since 3.13.0
788     */
789    @SuppressWarnings("unchecked")
790    public static <T> Class<T> getComponentType(final Class<T[]> cls) {
791        return cls == null ? null : (Class<T>) cls.getComponentType();
792    }
793
794    /**
795     * Gets the class name, handling {@code null} safely.
796     *
797     * @param cls The class for which to get the class name; may be null.
798     * @return The class name or the empty string in case the argument is {@code null}.
799     * @since 3.7
800     * @see Class#getSimpleName()
801     */
802    public static String getName(final Class<?> cls) {
803        return getName(cls, StringUtils.EMPTY);
804    }
805
806    /**
807     * Gets the class name, handling {@code null} safely.
808     *
809     * @param cls The class for which to get the class name; may be null.
810     * @param valueIfNull The return value if the argument {@code cls} is {@code null}.
811     * @return The class name or {@code valueIfNull}
812     * @since 3.7
813     * @see Class#getName()
814     */
815    public static String getName(final Class<?> cls, final String valueIfNull) {
816        return getName(cls, valueIfNull, false);
817    }
818
819    static String getName(final Class<?> cls, final String valueIfNull, final boolean simple) {
820        return cls == null ? valueIfNull : simple ? cls.getSimpleName() : cls.getName();
821    }
822
823    /**
824     * Gets the object's class name, handling {@code null} safely.
825     *
826     * @param object The object for which to get the class name; may be null.
827     * @return The class name or the empty String.
828     * @since 3.7
829     * @see Class#getSimpleName()
830     */
831    public static String getName(final Object object) {
832        return getName(object, StringUtils.EMPTY);
833    }
834
835    /**
836     * Gets the object's class name, handling {@code null} safely.
837     *
838     * @param object The object for which to get the class name; may be null.
839     * @param valueIfNull The value to return if {@code object} is {@code null}.
840     * @return The class name or {@code valueIfNull}.
841     * @since 3.0
842     * @see Class#getName()
843     */
844    public static String getName(final Object object, final String valueIfNull) {
845        return object == null ? valueIfNull : object.getClass().getName();
846    }
847
848    /**
849     * Gets the package name from the canonical name of a {@link Class}.
850     *
851     * @param cls The class to get the package name for, may be {@code null}.
852     * @return The package name or an empty string.
853     * @since 2.4
854     */
855    public static String getPackageCanonicalName(final Class<?> cls) {
856        if (cls == null) {
857            return StringUtils.EMPTY;
858        }
859        return getPackageCanonicalName(cls.getName());
860    }
861
862    /**
863     * Gets the package name from the class name of an {@link Object}.
864     *
865     * @param object The class to get the package name for, may be null.
866     * @param valueIfNull The value to return if null.
867     * @return The package name of the object, or the null value.
868     * @since 2.4
869     */
870    public static String getPackageCanonicalName(final Object object, final String valueIfNull) {
871        if (object == null) {
872            return valueIfNull;
873        }
874        return getPackageCanonicalName(object.getClass().getName());
875    }
876
877    /**
878     * Gets the package name from the class name.
879     *
880     * <p>
881     * The string passed in is assumed to be a class name - it is not checked.
882     * </p>
883     * <p>
884     * If the class is in the default package, return an empty string.
885     * </p>
886     *
887     * @param name The name to get the package name for, may be {@code null}.
888     * @return The package name or an empty string.
889     * @since 2.4
890     */
891    public static String getPackageCanonicalName(final String name) {
892        return getPackageName(getCanonicalName(name));
893    }
894
895    /**
896     * Gets the package name of a {@link Class}.
897     *
898     * @param cls The class to get the package name for, may be {@code null}.
899     * @return The package name or an empty string
900     */
901    public static String getPackageName(final Class<?> cls) {
902        if (cls == null) {
903            return StringUtils.EMPTY;
904        }
905        return getPackageName(cls.getName());
906    }
907
908    /**
909     * Gets the package name of an {@link Object}.
910     *
911     * @param object The class to get the package name for, may be null.
912     * @param valueIfNull The value to return if null.
913     * @return The package name of the object, or the null value.
914     */
915    public static String getPackageName(final Object object, final String valueIfNull) {
916        if (object == null) {
917            return valueIfNull;
918        }
919        return getPackageName(object.getClass());
920    }
921
922    /**
923     * Gets the package name from a {@link String}.
924     *
925     * <p>
926     * The string passed in is assumed to be a class name.
927     * </p>
928     * <p>
929     * If the class is unpackaged, return an empty string.
930     * </p>
931     *
932     * @param className The className to get the package name for, may be {@code null}.
933     * @return The package name or an empty string.
934     */
935    public static String getPackageName(String className) {
936        if (StringUtils.isEmpty(className)) {
937            return StringUtils.EMPTY;
938        }
939        int i = 0;
940        // Strip array encoding
941        while (className.charAt(i) == '[') {
942            i++;
943        }
944        className = className.substring(i);
945        // Strip Object type encoding
946        if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {
947            className = className.substring(1);
948        }
949        i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
950        if (i == -1) {
951            return StringUtils.EMPTY;
952        }
953        return className.substring(0, i);
954    }
955
956    /**
957     * Gets the primitive class for the given class name, for example "byte".
958     *
959     * @param className The primitive class for the given class name.
960     * @return The primitive class.
961     */
962    static Class<?> getPrimitiveClass(final String className) {
963        return NAME_PRIMITIVE_MAP.get(className);
964    }
965
966    /**
967     * Gets the desired Method much like {@code Class.getMethod}, however it ensures that the returned Method is from a
968     * public class or interface and not from an anonymous inner class. This means that the Method is invokable and doesn't
969     * fall foul of Java bug (<a href="https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4071957">4071957</a>).
970     *
971     * <pre>
972     *  {@code Set set = Collections.unmodifiableSet(...);
973     *  Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty",  new Class[0]);
974     *  Object result = method.invoke(set, new Object[]);}
975     * </pre>
976     *
977     * @param cls The class to check, not null.
978     * @param methodName The name of the method.
979     * @param parameterTypes The list of parameters.
980     * @return The method.
981     * @throws NullPointerException Thrown if the class is null.
982     * @throws SecurityException Thrown if a security violation occurred.
983     * @throws NoSuchMethodException Thrown if the method is not found in the given class or if the method doesn't conform with the
984     *         requirements.
985     */
986    public static Method getPublicMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) throws NoSuchMethodException {
987        final Method declaredMethod = cls.getMethod(methodName, parameterTypes);
988        if (isPublic(declaredMethod.getDeclaringClass())) {
989            return declaredMethod;
990        }
991        final List<Class<?>> candidateClasses = new ArrayList<>(getAllInterfaces(cls));
992        candidateClasses.addAll(getAllSuperclasses(cls));
993        for (final Class<?> candidateClass : candidateClasses) {
994            if (!isPublic(candidateClass)) {
995                continue;
996            }
997            final Method candidateMethod;
998            try {
999                candidateMethod = candidateClass.getMethod(methodName, parameterTypes);
1000            } catch (final NoSuchMethodException ex) {
1001                continue;
1002            }
1003            if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) {
1004                return candidateMethod;
1005            }
1006        }
1007        throw new NoSuchMethodException("Can't find a public method for " + methodName + " " + ArrayUtils.toString(parameterTypes));
1008    }
1009
1010    /**
1011     * Gets the canonical name minus the package name from a {@link Class}.
1012     *
1013     * @param cls The class for which to get the short canonical class name; may be null.
1014     * @return The canonical name without the package name or an empty string.
1015     * @since 2.4
1016     * @see Class#getCanonicalName()
1017     */
1018    public static String getShortCanonicalName(final Class<?> cls) {
1019        return cls == null ? StringUtils.EMPTY : getShortCanonicalName(cls.getCanonicalName());
1020    }
1021
1022    /**
1023     * Gets the canonical name minus the package name for an {@link Object}.
1024     *
1025     * @param object The class to get the short name for, may be null.
1026     * @param valueIfNull The value to return if null.
1027     * @return The canonical name of the object without the package name, or the null value.
1028     * @since 2.4
1029     * @see Class#getCanonicalName()
1030     */
1031    public static String getShortCanonicalName(final Object object, final String valueIfNull) {
1032        return object == null ? valueIfNull : getShortCanonicalName(object.getClass());
1033    }
1034
1035    /**
1036     * Gets the canonical name minus the package name from a String.
1037     *
1038     * <p>
1039     * The string passed in is assumed to be a class name - it is not checked.
1040     * </p>
1041     *
1042     * <p>
1043     * Note that this method is mainly designed to handle the arrays and primitives properly. If the class is an inner class
1044     * then the result value will not contain the outer classes. This way the behavior of this method is different from
1045     * {@link #getShortClassName(String)}. The argument in that case is class name and not canonical name and the return
1046     * value retains the outer classes.
1047     * </p>
1048     *
1049     * <p>
1050     * Note that there is no way to reliably identify the part of the string representing the package hierarchy and the part
1051     * that is the outer class or classes in case of an inner class. Trying to find the class would require reflective call
1052     * and the class itself may not even be on the class path. Relying on the fact that class names start with capital
1053     * letter and packages with lower case is heuristic.
1054     * </p>
1055     *
1056     * <p>
1057     * It is recommended to use {@link #getShortClassName(String)} for cases when the class is an inner class and use this
1058     * method for cases it is designed for.
1059     * </p>
1060     *
1061     * <table>
1062     * <caption>Examples</caption>
1063     * <tr>
1064     * <td>return value</td>
1065     * <td>input</td>
1066     * </tr>
1067     * <tr>
1068     * <td>{@code ""}</td>
1069     * <td>{@code (String) null}</td>
1070     * </tr>
1071     * <tr>
1072     * <td>{@code "Map.Entry"}</td>
1073     * <td>{@code java.util.Map.Entry.class.getName()}</td>
1074     * </tr>
1075     * <tr>
1076     * <td>{@code "Entry"}</td>
1077     * <td>{@code java.util.Map.Entry.class.getCanonicalName()}</td>
1078     * </tr>
1079     * <tr>
1080     * <td>{@code "ClassUtils"}</td>
1081     * <td>{@code "org.apache.commons.lang3.ClassUtils"}</td>
1082     * </tr>
1083     * <tr>
1084     * <td>{@code "ClassUtils[]"}</td>
1085     * <td>{@code "[Lorg.apache.commons.lang3.ClassUtils;"}</td>
1086     * </tr>
1087     * <tr>
1088     * <td>{@code "ClassUtils[][]"}</td>
1089     * <td>{@code "[[Lorg.apache.commons.lang3.ClassUtils;"}</td>
1090     * </tr>
1091     * <tr>
1092     * <td>{@code "ClassUtils[]"}</td>
1093     * <td>{@code "org.apache.commons.lang3.ClassUtils[]"}</td>
1094     * </tr>
1095     * <tr>
1096     * <td>{@code "ClassUtils[][]"}</td>
1097     * <td>{@code "org.apache.commons.lang3.ClassUtils[][]"}</td>
1098     * </tr>
1099     * <tr>
1100     * <td>{@code "int[]"}</td>
1101     * <td>{@code "[I"}</td>
1102     * </tr>
1103     * <tr>
1104     * <td>{@code "int[]"}</td>
1105     * <td>{@code int[].class.getCanonicalName()}</td>
1106     * </tr>
1107     * <tr>
1108     * <td>{@code "int[]"}</td>
1109     * <td>{@code int[].class.getName()}</td>
1110     * </tr>
1111     * <tr>
1112     * <td>{@code "int[][]"}</td>
1113     * <td>{@code "[[I"}</td>
1114     * </tr>
1115     * <tr>
1116     * <td>{@code "int[]"}</td>
1117     * <td>{@code "int[]"}</td>
1118     * </tr>
1119     * <tr>
1120     * <td>{@code "int[][]"}</td>
1121     * <td>{@code "int[][]"}</td>
1122     * </tr>
1123     * </table>
1124     *
1125     * @param canonicalName The class name to get the short name for.
1126     * @return The canonical name of the class without the package name or an empty string.
1127     * @since 2.4
1128     */
1129    public static String getShortCanonicalName(final String canonicalName) {
1130        return getShortClassName(getCanonicalName(canonicalName));
1131    }
1132
1133    /**
1134     * Gets the class name minus the package name from a {@link Class}.
1135     *
1136     * @param cls The class to get the short name for.
1137     * @return The class name without the package name or an empty string. If the class is an inner class then the returned
1138     *         value will contain the outer class or classes separated with {@code .} (dot) character.
1139     */
1140    public static String getShortClassName(final Class<?> cls) {
1141        if (cls == null) {
1142            return StringUtils.EMPTY;
1143        }
1144        int dim = 0;
1145        Class<?> c = cls;
1146        while (c.isArray()) {
1147            dim++;
1148            c = c.getComponentType();
1149        }
1150        String base;
1151        // c.isAnonymousClass() / isLocalClass() and the getDeclaringClass() chain
1152        // can both throw NoClassDefFoundError when the enclosing class is
1153        // missing from the classpath, so the try/catch wraps the whole block.
1154        try {
1155            final Deque<String> parts = new ArrayDeque<>();
1156            Class<?> x = c;
1157            // An anonymous or local class anywhere in the chain stops the walk: it has no declaring
1158            // class, so the chain cannot recover its enclosing context. Use the name-based form for
1159            // that enclosure only, which keeps the context and the compiler ordinals ($13, $10Named, etc.),
1160            // and keep the simple names of the member classes below it, which may contain '$'.
1161            while (x != null && !x.isAnonymousClass() && !x.isLocalClass()) {
1162                parts.push(x.getSimpleName());
1163                x = x.getDeclaringClass();
1164            }
1165            if (x != null) {
1166                parts.push(getShortClassName(x.getName()));
1167            }
1168            base = String.join(".", parts);
1169        } catch (final NoClassDefFoundError ignored) {
1170            base = getShortClassName(c.getName());
1171        }
1172        return base + StringUtils.repeat("[]", dim);
1173    }
1174
1175    /**
1176     * Gets the class name of the {@code object} without the package name or names.
1177     *
1178     * @param object The class to get the short name for, may be {@code null}.
1179     * @param valueIfNull The value to return if the object is {@code null}.
1180     * @return The class name of the object without the package name, or {@code valueIfNull} if the argument {@code object}
1181     *         is {@code null}.
1182     */
1183    public static String getShortClassName(final Object object, final String valueIfNull) {
1184        if (object == null) {
1185            return valueIfNull;
1186        }
1187        return getShortClassName(object.getClass());
1188    }
1189
1190    /**
1191     * Gets the class name minus the package name from a String.
1192     *
1193     * <p>
1194     * The string passed in is assumed to be a class name - it is not checked. The string has to be formatted the way as the
1195     * JDK method {@code Class.getName()} returns it, and not the usual way as we write it, for example in import
1196     * statements, or as it is formatted by {@code Class.getCanonicalName()}.
1197     * </p>
1198     *
1199     * <p>
1200     * The difference is significant only in case of classes that are inner classes of some other classes. In this case
1201     * the separator between the outer and inner class (possibly on multiple hierarchy level) has to be {@code $} (dollar
1202     * sign) and not {@code .} (dot), as it is returned by {@code Class.getName()}
1203     * </p>
1204     *
1205     * <p>
1206     * Note that this method is called from the {@link #getShortClassName(Class)} method using the string returned by
1207     * {@code Class.getName()}.
1208     * </p>
1209     *
1210     * <p>
1211     * Note that this method differs from {@link #getSimpleName(Class)} in that this will return, for example
1212     * {@code "Map.Entry"} whilst the {@link Class} variant will simply return {@code "Entry"}. In this example
1213     * the argument {@code className} is the string {@code java.util.Map$Entry} (note the {@code $} sign).
1214     * </p>
1215     *
1216     * @param className The className to get the short name for. It has to be formatted as returned by
1217     *        {@code Class.getName()} and not {@code Class.getCanonicalName()}.
1218     * @return The class name of the class without the package name or an empty string. If the class is an inner class then
1219     *         value contains the outer class or classes and the separator is replaced to be {@code .} (dot) character.
1220     */
1221    public static String getShortClassName(String className) {
1222        if (StringUtils.isEmpty(className)) {
1223            return StringUtils.EMPTY;
1224        }
1225        final StringBuilder arrayPrefix = new StringBuilder();
1226        // Handle array encoding
1227        if (className.startsWith("[")) {
1228            while (className.charAt(0) == '[') {
1229                className = className.substring(1);
1230                arrayPrefix.append("[]");
1231            }
1232            // Strip Object type encoding
1233            if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {
1234                className = className.substring(1, className.length() - 1);
1235            }
1236            if (REVERSE_ABBREVIATION_MAP.containsKey(className)) {
1237                className = REVERSE_ABBREVIATION_MAP.get(className);
1238            }
1239        }
1240        final int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
1241        final int innerIdx = className.indexOf(INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1);
1242        String out = className.substring(lastDotIdx + 1);
1243        if (innerIdx != -1) {
1244            out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR);
1245        }
1246        return out + arrayPrefix;
1247    }
1248
1249    /**
1250     * Gets the simple class name, handling {@code null} safely.
1251     *
1252     * @param cls The class for which to get the simple name; may be null.
1253     * @return The simple class name or the empty string in case the argument is {@code null}.
1254     * @since 3.0
1255     * @see Class#getSimpleName()
1256     */
1257    public static String getSimpleName(final Class<?> cls) {
1258        return getSimpleName(cls, StringUtils.EMPTY);
1259    }
1260
1261    /**
1262     * Gets the simple class name, handling {@code null} safely.
1263     *
1264     * @param cls The class for which to get the simple name; may be null.
1265     * @param valueIfNull The value to return if null.
1266     * @return The simple class name or {@code valueIfNull} if the argument {@code cls} is {@code null}.
1267     * @since 3.0
1268     * @see Class#getSimpleName()
1269     */
1270    public static String getSimpleName(final Class<?> cls, final String valueIfNull) {
1271        return cls == null ? valueIfNull : cls.getSimpleName();
1272    }
1273
1274    /**
1275     * Gets the object's simple class name, handling {@code null} safely.
1276     *
1277     * <p>
1278     * It is to note that this method is overloaded and in case the argument {@code object} is a {@link Class} object then
1279     * the {@link #getSimpleName(Class)} will be invoked. If this is a significant possibility then the caller should check
1280     * this case and call {@code
1281     * getSimpleName(Class.class)} or just simply use the string literal {@code "Class"}, which is the result of the method
1282     * in that case.
1283     * </p>
1284     *
1285     * @param object The object for which to get the simple class name; may be null.
1286     * @return The simple class name or the empty string in case the argument is {@code null}.
1287     * @since 3.7
1288     * @see Class#getSimpleName()
1289     */
1290    public static String getSimpleName(final Object object) {
1291        return getSimpleName(object, StringUtils.EMPTY);
1292    }
1293
1294    /**
1295     * Gets the object's simple class name, handling {@code null} safely.
1296     *
1297     * @param object The object for which to get the simple class name; may be null.
1298     * @param valueIfNull The value to return if {@code object} is {@code null}.
1299     * @return The simple class name or {@code valueIfNull} if the argument {@code object} is {@code null}.
1300     * @since 3.0
1301     * @see Class#getSimpleName()
1302     */
1303    public static String getSimpleName(final Object object, final String valueIfNull) {
1304        return object == null ? valueIfNull : object.getClass().getSimpleName();
1305    }
1306
1307    /**
1308     * Gets an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order,
1309     * excluding interfaces.
1310     *
1311     * @param type The type to get the class hierarchy from.
1312     * @return Iterable an Iterable over the class hierarchy of the given class.
1313     * @since 3.2
1314     */
1315    public static Iterable<Class<?>> hierarchy(final Class<?> type) {
1316        return hierarchy(type, Interfaces.EXCLUDE);
1317    }
1318
1319    /**
1320     * Gets an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order.
1321     *
1322     * @param type The type to get the class hierarchy from.
1323     * @param interfacesBehavior switch indicating whether to include or exclude interfaces.
1324     * @return Iterable an Iterable over the class hierarchy of the given class.
1325     * @since 3.2
1326     */
1327    public static Iterable<Class<?>> hierarchy(final Class<?> type, final Interfaces interfacesBehavior) {
1328        final Iterable<Class<?>> classes = () -> {
1329            final AtomicReference<Class<?>> next = new AtomicReference<>(type);
1330            return new Iterator<Class<?>>() {
1331
1332                @Override
1333                public boolean hasNext() {
1334                    return next.get() != null;
1335                }
1336
1337                @Override
1338                public Class<?> next() {
1339                    return next.getAndUpdate(Class::getSuperclass);
1340                }
1341
1342                @Override
1343                public void remove() {
1344                    throw new UnsupportedOperationException();
1345                }
1346
1347            };
1348        };
1349        if (interfacesBehavior != Interfaces.INCLUDE) {
1350            return classes;
1351        }
1352        return () -> {
1353            final Set<Class<?>> seenInterfaces = new HashSet<>();
1354            final Iterator<Class<?>> wrapped = classes.iterator();
1355
1356            return new Iterator<Class<?>>() {
1357                Iterator<Class<?>> interfaces = Collections.emptyIterator();
1358
1359                @Override
1360                public boolean hasNext() {
1361                    return interfaces.hasNext() || wrapped.hasNext();
1362                }
1363
1364                @Override
1365                public Class<?> next() {
1366                    if (interfaces.hasNext()) {
1367                        final Class<?> nextInterface = interfaces.next();
1368                        seenInterfaces.add(nextInterface);
1369                        return nextInterface;
1370                    }
1371                    final Class<?> nextSuperclass = wrapped.next();
1372                    final Set<Class<?>> currentInterfaces = new LinkedHashSet<>();
1373                    walkInterfaces(currentInterfaces, nextSuperclass);
1374                    interfaces = currentInterfaces.iterator();
1375                    return nextSuperclass;
1376                }
1377
1378                @Override
1379                public void remove() {
1380                    throw new UnsupportedOperationException();
1381                }
1382
1383                private void walkInterfaces(final Set<Class<?>> addTo, final Class<?> c) {
1384                    for (final Class<?> iface : c.getInterfaces()) {
1385                        if (!seenInterfaces.contains(iface)) {
1386                            addTo.add(iface);
1387                        }
1388                        walkInterfaces(addTo, iface);
1389                    }
1390                }
1391
1392            };
1393        };
1394    }
1395
1396    /**
1397     * Tests whether one {@link Class} can be assigned to a variable of another {@link Class}.
1398     *
1399     * <p>
1400     * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into account widenings of
1401     * primitive classes and {@code null}s.
1402     * </p>
1403     *
1404     * <p>
1405     * Primitive widenings allow an int to be assigned to a long, float or double. This method returns the correct result
1406     * for these cases.
1407     * </p>
1408     *
1409     * <p>
1410     * {@code null} may be assigned to any reference type. This method will return {@code true} if {@code null} is passed in
1411     * and the toClass is non-primitive.
1412     * </p>
1413     *
1414     * <p>
1415     * Specifically, this method tests whether the type represented by the specified {@link Class} parameter can be
1416     * converted to the type represented by this {@link Class} object via an identity conversion widening primitive or
1417     * widening reference conversion. See <em><a href="https://docs.oracle.com/javase/specs/">The Java Language
1418     * Specification</a></em>, sections 5.1.1, 5.1.2 and 5.1.4 for details.
1419     * </p>
1420     *
1421     * <p>
1422     * <strong>Since Lang 3.0,</strong> this method will default behavior for calculating assignability between primitive
1423     * and wrapper types <em>corresponding to the running Java version</em>; i.e. autoboxing will be the default behavior in
1424     * VMs running Java versions &gt; 1.5.
1425     * </p>
1426     *
1427     * @param cls The Class to check, may be null.
1428     * @param toClass The Class to try to assign into, returns false if null.
1429     * @return {@code true} if assignment possible.
1430     */
1431    public static boolean isAssignable(final Class<?> cls, final Class<?> toClass) {
1432        return isAssignable(cls, toClass, true);
1433    }
1434
1435    /**
1436     * Tests whether one {@link Class} can be assigned to a variable of another {@link Class}.
1437     *
1438     * <p>
1439     * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into account widenings of
1440     * primitive classes and {@code null}s.
1441     * </p>
1442     *
1443     * <p>
1444     * Primitive widenings allow an int to be assigned to a long, float or double. This method returns the correct result
1445     * for these cases.
1446     * </p>
1447     *
1448     * <p>
1449     * {@code null} may be assigned to any reference type. This method will return {@code true} if {@code null} is passed in
1450     * and the toClass is non-primitive.
1451     * </p>
1452     *
1453     * <p>
1454     * Specifically, this method tests whether the type represented by the specified {@link Class} parameter can be
1455     * converted to the type represented by this {@link Class} object via an identity conversion widening primitive or
1456     * widening reference conversion. See <em><a href="https://docs.oracle.com/javase/specs/">The Java Language
1457     * Specification</a></em>, sections 5.1.1, 5.1.2 and 5.1.4 for details.
1458     * </p>
1459     *
1460     * @param cls The Class to check, may be null.
1461     * @param toClass The Class to try to assign into, returns false if null.
1462     * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers.
1463     * @return {@code true} if assignment possible.
1464     */
1465    public static boolean isAssignable(Class<?> cls, final Class<?> toClass, final boolean autoboxing) {
1466        if (toClass == null) {
1467            return false;
1468        }
1469        // have to check for null, as isAssignableFrom doesn't
1470        if (cls == null) {
1471            return !toClass.isPrimitive();
1472        }
1473        // autoboxing:
1474        if (autoboxing) {
1475            if (cls.isPrimitive() && !toClass.isPrimitive()) {
1476                cls = primitiveToWrapper(cls);
1477                if (cls == null) {
1478                    return false;
1479                }
1480            }
1481            if (toClass.isPrimitive() && !cls.isPrimitive()) {
1482                cls = wrapperToPrimitive(cls);
1483                if (cls == null) {
1484                    return false;
1485                }
1486            }
1487        }
1488        if (cls.equals(toClass)) {
1489            return true;
1490        }
1491        if (cls.isPrimitive()) {
1492            if (!toClass.isPrimitive()) {
1493                return false;
1494            }
1495            if (Integer.TYPE.equals(cls)) {
1496                return Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass);
1497            }
1498            if (Long.TYPE.equals(cls)) {
1499                return Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass);
1500            }
1501            if (Boolean.TYPE.equals(cls)) {
1502                return false;
1503            }
1504            if (Double.TYPE.equals(cls)) {
1505                return false;
1506            }
1507            if (Float.TYPE.equals(cls)) {
1508                return Double.TYPE.equals(toClass);
1509            }
1510            if (Character.TYPE.equals(cls)  || Short.TYPE.equals(cls)) {
1511                return Integer.TYPE.equals(toClass) || Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass) || Double.TYPE.equals(toClass);
1512            }
1513            if (Byte.TYPE.equals(cls)) {
1514                return Short.TYPE.equals(toClass) || Integer.TYPE.equals(toClass) || Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass)
1515                    || Double.TYPE.equals(toClass);
1516            }
1517            // should never get here
1518            return false;
1519        }
1520        return toClass.isAssignableFrom(cls);
1521    }
1522
1523    /**
1524     * Tests whether an array of Classes can be assigned to another array of Classes.
1525     *
1526     * <p>
1527     * This method calls {@link #isAssignable(Class, Class) isAssignable} for each Class pair in the input arrays. It can be
1528     * used to check if a set of arguments (the first parameter) are suitably compatible with a set of method parameter
1529     * types (the second parameter).
1530     * </p>
1531     *
1532     * <p>
1533     * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into account widenings of
1534     * primitive classes and {@code null}s.
1535     * </p>
1536     *
1537     * <p>
1538     * Primitive widenings allow an int to be assigned to a {@code long}, {@code float} or {@code double}. This method
1539     * returns the correct result for these cases.
1540     * </p>
1541     *
1542     * <p>
1543     * {@code null} may be assigned to any reference type. This method will return {@code true} if {@code null} is passed in
1544     * and the toClass is non-primitive.
1545     * </p>
1546     *
1547     * <p>
1548     * Specifically, this method tests whether the type represented by the specified {@link Class} parameter can be
1549     * converted to the type represented by this {@link Class} object via an identity conversion widening primitive or
1550     * widening reference conversion. See <em><a href="https://docs.oracle.com/javase/specs/">The Java Language
1551     * Specification</a></em>, sections 5.1.1, 5.1.2 and 5.1.4 for details.
1552     * </p>
1553     *
1554     * <p>
1555     * <strong>Since Lang 3.0,</strong> this method will default behavior for calculating assignability between primitive
1556     * and wrapper types <em>corresponding to the running Java version</em>; i.e. autoboxing will be the default behavior in
1557     * VMs running Java versions &gt; 1.5.
1558     * </p>
1559     *
1560     * @param classArray The array of Classes to check, may be {@code null}.
1561     * @param toClassArray The array of Classes to try to assign into, may be {@code null}.
1562     * @return {@code true} if assignment possible.
1563     */
1564    public static boolean isAssignable(final Class<?>[] classArray, final Class<?>... toClassArray) {
1565        return isAssignable(classArray, toClassArray, true);
1566    }
1567
1568    /**
1569     * Tests whether an array of Classes can be assigned to another array of Classes.
1570     *
1571     * <p>
1572     * This method calls {@link #isAssignable(Class, Class) isAssignable} for each Class pair in the input arrays. It can be
1573     * used to check if a set of arguments (the first parameter) are suitably compatible with a set of method parameter
1574     * types (the second parameter).
1575     * </p>
1576     *
1577     * <p>
1578     * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this method takes into account widenings of
1579     * primitive classes and {@code null}s.
1580     * </p>
1581     *
1582     * <p>
1583     * Primitive widenings allow an int to be assigned to a {@code long}, {@code float} or {@code double}. This method
1584     * returns the correct result for these cases.
1585     * </p>
1586     *
1587     * <p>
1588     * {@code null} may be assigned to any reference type. This method will return {@code true} if {@code null} is passed in
1589     * and the toClass is non-primitive.
1590     * </p>
1591     *
1592     * <p>
1593     * Specifically, this method tests whether the type represented by the specified {@link Class} parameter can be
1594     * converted to the type represented by this {@link Class} object via an identity conversion widening primitive or
1595     * widening reference conversion. See <em><a href="https://docs.oracle.com/javase/specs/">The Java Language
1596     * Specification</a></em>, sections 5.1.1, 5.1.2 and 5.1.4 for details.
1597     * </p>
1598     *
1599     * @param classArray The array of Classes to check, may be {@code null}
1600     * @param toClassArray The array of Classes to try to assign into, may be {@code null}
1601     * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers
1602     * @return {@code true} if assignment possible
1603     */
1604    public static boolean isAssignable(Class<?>[] classArray, Class<?>[] toClassArray, final boolean autoboxing) {
1605        if (!ArrayUtils.isSameLength(classArray, toClassArray)) {
1606            return false;
1607        }
1608        classArray = ArrayUtils.nullToEmpty(classArray);
1609        toClassArray = ArrayUtils.nullToEmpty(toClassArray);
1610        for (int i = 0; i < classArray.length; i++) {
1611            if (!isAssignable(classArray[i], toClassArray[i], autoboxing)) {
1612                return false;
1613            }
1614        }
1615        return true;
1616    }
1617
1618    /**
1619     * Tests whether the specified class an inner class or static nested class.
1620     *
1621     * @param cls The class to check, may be null.
1622     * @return {@code true} if the class is an inner or static nested class, false if not or {@code null}.
1623     */
1624    public static boolean isInnerClass(final Class<?> cls) {
1625        return cls != null && cls.getEnclosingClass() != null;
1626    }
1627
1628    /**
1629     * Tests whether the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte},
1630     * {@link Character}, {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
1631     *
1632     * @param type The class to query or null.
1633     * @return true if the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte},
1634     *         {@link Character}, {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
1635     * @since 3.1
1636     */
1637    public static boolean isPrimitiveOrWrapper(final Class<?> type) {
1638        return type != null && type.isPrimitive() || isPrimitiveWrapper(type);
1639    }
1640
1641    /**
1642     * Tests whether the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character},
1643     * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
1644     *
1645     * @param type The class to query or null.
1646     * @return true if the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character},
1647     *         {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}).
1648     * @since 3.1
1649     */
1650    public static boolean isPrimitiveWrapper(final Class<?> type) {
1651        return WRAPPER_PRIMITIVE_MAP.containsKey(type);
1652    }
1653
1654    /**
1655     * Tests whether a {@link Class} is public.
1656     *
1657     * @param cls Class to test.
1658     * @return {@code true} if {@code cls} is public.
1659     * @since 3.13.0
1660     */
1661    public static boolean isPublic(final Class<?> cls) {
1662        return Modifier.isPublic(cls.getModifiers());
1663    }
1664
1665    /**
1666     * Converts the specified array of primitive Class objects to an array of its corresponding wrapper Class objects.
1667     *
1668     * @param classes The class array to convert, may be null or empty.
1669     * @return An array which contains for each given class, the wrapper class or the original class if class is not a primitive. {@code null} if null input.
1670     *         Empty array if an empty array passed in.
1671     * @since 2.1
1672     */
1673    public static Class<?>[] primitivesToWrappers(final Class<?>... classes) {
1674        if (classes == null) {
1675            return null;
1676        }
1677        if (classes.length == 0) {
1678            return classes;
1679        }
1680        return ArrayUtils.setAll(new Class[classes.length], i -> primitiveToWrapper(classes[i]));
1681    }
1682
1683    /**
1684     * Converts the specified primitive Class object to its corresponding wrapper Class object.
1685     *
1686     * <p>
1687     * NOTE: From v2.2, this method handles {@code Void.TYPE}, returning {@code Void.TYPE}.
1688     * </p>
1689     *
1690     * @param cls The class to convert, may be null.
1691     * @return The wrapper class for {@code cls} or {@code cls} if {@code cls} is not a primitive. {@code null} if null input.
1692     * @since 2.1
1693     */
1694    public static Class<?> primitiveToWrapper(final Class<?> cls) {
1695        return cls != null && cls.isPrimitive() ? PRIMITIVE_WRAPPER_MAP.get(cls) : cls;
1696    }
1697
1698    /**
1699     * Converts an array of {@link Object} in to an array of {@link Class} objects. If any of these objects is null, a null element will be inserted into the
1700     * array.
1701     *
1702     * <p>
1703     * This method returns {@code null} for a {@code null} input array.
1704     * </p>
1705     *
1706     * @param array An {@link Object} array.
1707     * @return A {@link Class} array, {@code null} if null array input.
1708     * @since 2.4
1709     */
1710    public static Class<?>[] toClass(final Object... array) {
1711        if (array == null) {
1712            return null;
1713        }
1714        if (array.length == 0) {
1715            return ArrayUtils.EMPTY_CLASS_ARRAY;
1716        }
1717        return ArrayUtils.setAll(new Class[array.length], i -> array[i] == null ? null : array[i].getClass());
1718    }
1719
1720    /**
1721     * Converts and cleans up a class name to a JLS style class name.
1722     * <p>
1723     * The provided class name is normalized by removing all whitespace. This is especially helpful when handling XML element values in which whitespace has not
1724     * been collapsed.
1725     * </p>
1726     *
1727     * @param className The class name.
1728     * @return The converted name.
1729     * @throws NullPointerException     Thrown if the className is null.
1730     * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
1731     * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
1732     * @see <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.4.1">JVM: Array dimension limits in JVM Specification
1733     *      CONSTANT_Class_info</a>
1734     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7">JLS: Fully Qualified Names and Canonical Names</a>
1735     * @see <a href="https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1">JLS: The Form of a Binary</a>
1736     */
1737    private static String toCleanName(final String className) {
1738        return toEncodedName(StringUtils.deleteWhitespace(className));
1739    }
1740
1741    /**
1742     * Converts a class name to a JLS style class name without normalizing whitespace.
1743     *
1744     * @param className The class name.
1745     * @return The converted name.
1746     * @throws NullPointerException     Thrown if the className is null.
1747     * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
1748     * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
1749     */
1750    private static String toEncodedName(final String className) {
1751        String canonicalName = className;
1752        Objects.requireNonNull(canonicalName, "className");
1753        if (canonicalName.isEmpty()) {
1754            throw new IllegalArgumentException("Class name is empty");
1755        }
1756        final String encodedArrayOpen = "[";
1757        final String encodedClassNameStart = "L";
1758        final String encodedClassNameEnd = ";";
1759        final boolean encodedName = canonicalName.startsWith(encodedArrayOpen) && canonicalName.endsWith(encodedClassNameEnd);
1760        if (encodedName) {
1761            final int arrIdx = canonicalName.indexOf(encodedClassNameStart);
1762            if (arrIdx > MAX_JVM_ARRAY_DIMENSION) {
1763                throw new IllegalArgumentException("Array dimension greater than JVM specification maximum of 255.");
1764            }
1765            if (arrIdx < 0) {
1766                throw new IllegalArgumentException("Expected 'L' after '[' for an array style string.");
1767            }
1768            final int cnLen = canonicalName.length() - (arrIdx + 2); // account for the ending ';'
1769            if (cnLen > MAX_CLASS_NAME_LENGTH) {
1770                throw new IllegalArgumentException(String.format("Class name greater than maximum length %,d", MAX_CLASS_NAME_LENGTH));
1771            }
1772        }
1773        final String arrayMarker = "[]";
1774        final int arrIdx = canonicalName.indexOf(arrayMarker);
1775        // The class name length without array markers.
1776        final int cnLen = arrIdx > 0 ? arrIdx : canonicalName.length();
1777        if (cnLen > MAX_CLASS_NAME_LENGTH && !encodedName) {
1778            throw new IllegalArgumentException(String.format("Class name greater than maximum length %,d", MAX_CLASS_NAME_LENGTH));
1779        }
1780        if (canonicalName.endsWith(arrayMarker)) {
1781            // Reject malformed inputs like "java.lang.String[]junk[]" or
1782            // "java.lang.String[]][]" where the suffix is not composed of
1783            // repeated "[]" pairs.
1784            final String tail = canonicalName.substring(arrIdx);
1785            if (!ARRAY_TAIL_PATTERN.matcher(tail).matches()) {
1786                throw new IllegalArgumentException("Malformed array name: " + canonicalName);
1787            }
1788            final int dims =  (canonicalName.length() - arrIdx) / 2;
1789            if (dims > MAX_JVM_ARRAY_DIMENSION) {
1790                throw new IllegalArgumentException("Array dimension greater than JVM specification maximum of 255.");
1791            }
1792            final StringBuilder classNameBuffer = new StringBuilder(StringUtils.repeat(encodedArrayOpen, dims));
1793            canonicalName = canonicalName.substring(0, arrIdx);
1794            final String abbreviation = ABBREVIATION_MAP.get(canonicalName);
1795            if (abbreviation != null) {
1796                classNameBuffer.append(abbreviation);
1797            } else {
1798                classNameBuffer.append(encodedClassNameStart).append(canonicalName).append(encodedClassNameEnd);
1799            }
1800            canonicalName = classNameBuffer.toString();
1801        }
1802        return canonicalName;
1803    }
1804
1805    /**
1806     * Decides if the part that was just copied to its destination location in the work array can be kept as it was copied
1807     * or must be abbreviated. It must be kept when the part is the last one, which is the simple name of the class. In this
1808     * case the {@code source} index, from where the characters are copied points one position after the last character,
1809     * a.k.a. {@code source ==
1810     * originalLength}
1811     *
1812     * <p>
1813     * If the part is not the last one then it can be kept unabridged if the number of the characters copied so far plus the
1814     * character that are to be copied is less than or equal to the desired length.
1815     * </p>
1816     *
1817     * @param runAheadTarget The target index (where the characters were copied to) pointing after the last character copied
1818     *        when the current part was copied.
1819     * @param source The source index (where the characters were copied from) pointing after the last character copied when
1820     *        the current part was copied.
1821     * @param originalLength The original length of the class full name, which is abbreviated.
1822     * @param desiredLength The desired length of the abbreviated class name.
1823     * @return {@code true} if it can be kept in its original length; {@code false} if the current part has to be abbreviated.
1824     */
1825    private static boolean useFull(final int runAheadTarget, final int source, final int originalLength, final int desiredLength) {
1826        return source >= originalLength || runAheadTarget + originalLength - source <= desiredLength;
1827    }
1828
1829    /**
1830     * Converts the specified array of wrapper Class objects to an array of its corresponding primitive Class objects.
1831     *
1832     * <p>
1833     * This method invokes {@code wrapperToPrimitive()} for each element of the passed in array.
1834     * </p>
1835     *
1836     * @param classes The class array to convert, may be null or empty.
1837     * @return An array which contains for each given class, the primitive class or {@code null} if the original class is not a wrapper class.
1838     *         {@code null} if null input. Empty array if an empty array passed in.
1839     * @see #wrapperToPrimitive(Class)
1840     * @since 2.4
1841     */
1842    public static Class<?>[] wrappersToPrimitives(final Class<?>... classes) {
1843        if (classes == null) {
1844            return null;
1845        }
1846        if (classes.length == 0) {
1847            return classes;
1848        }
1849        return ArrayUtils.setAll(new Class[classes.length], i -> wrapperToPrimitive(classes[i]));
1850    }
1851
1852    /**
1853     * Converts the specified wrapper class to its corresponding primitive class.
1854     *
1855     * <p>
1856     * This method is the counter part of {@code primitiveToWrapper()}. If the passed in class is a wrapper class for a
1857     * primitive type, this primitive type will be returned (e.g. {@code Integer.TYPE} for {@code Integer.class}). For other
1858     * classes, or if the parameter is {@code null}, the return value is {@code null}.
1859     * </p>
1860     *
1861     * @param cls The class to convert, may be {@code null}.
1862     * @return The corresponding primitive type if {@code cls} is a wrapper class, {@code null} otherwise.
1863     * @see #primitiveToWrapper(Class)
1864     * @since 2.4
1865     */
1866    public static Class<?> wrapperToPrimitive(final Class<?> cls) {
1867        return WRAPPER_PRIMITIVE_MAP.get(cls);
1868    }
1869
1870    /**
1871     * ClassUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
1872     * {@code ClassUtils.getShortClassName(cls)}.
1873     *
1874     * <p>
1875     * This constructor is public to permit tools that require a JavaBean instance to operate.
1876     * </p>
1877     *
1878     * @deprecated TODO Make private in 4.0.
1879     */
1880    @Deprecated
1881    public ClassUtils() {
1882        // empty
1883    }
1884
1885}