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 '.' == {@value}}. 099 */ 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 // 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 > 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 > 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}