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