4949public class ClassUtils {
5050
5151 /**
52- * The JVM {@code CONSTANT_Class_info} structure defines an array type descriptor is valid only if it represents 255 or fewer dimensions .
52+ * The JLS-specified maximum class name length {@value} .
5353 *
54+ * @see Class#forName(String, boolean, ClassLoader)
5455 * @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>
56+ * @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>
57+ * @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>
58+ */
59+ private static final int MAX_CLASS_NAME_LENGTH = 65535 ;
60+
61+ /**
62+ * The JVM-specified {@code CONSTANT_Class_info} structure defines an array type descriptor is valid only if it represents {@value} or fewer dimensions.
63+ *
64+ * @see Class#forName(String, boolean, ClassLoader)
65+ * @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>
66+ * @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>
67+ * @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>
5568 */
5669 private static final int MAX_JVM_ARRAY_DIMENSION = 255 ;
5770
@@ -533,6 +546,7 @@ private static String getCanonicalName(final String name) {
533546 * @throws NullPointerException if the className is null.
534547 * @throws ClassNotFoundException if the class is not found.
535548 * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
549+ * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
536550 * @see Class#forName(String, boolean, ClassLoader)
537551 * @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>
538552 * @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>
@@ -554,6 +568,7 @@ public static Class<?> getClass(final ClassLoader classLoader, final String clas
554568 * @throws NullPointerException if the className is null.
555569 * @throws ClassNotFoundException if the class is not found.
556570 * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
571+ * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
557572 * @see Class#forName(String, boolean, ClassLoader)
558573 * @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>
559574 * @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>
@@ -566,7 +581,7 @@ public static Class<?> getClass(final ClassLoader classLoader, final String clas
566581 do {
567582 try {
568583 final Class <?> clazz = getPrimitiveClass (next );
569- return clazz != null ? clazz : Class .forName (toCanonicalName (next ), initialize , classLoader );
584+ return clazz != null ? clazz : Class .forName (toCleanName (next ), initialize , classLoader );
570585 } catch (final ClassNotFoundException ex ) {
571586 lastDotIndex = next .lastIndexOf (PACKAGE_SEPARATOR_CHAR );
572587 if (lastDotIndex != -1 ) {
@@ -587,6 +602,7 @@ public static Class<?> getClass(final ClassLoader classLoader, final String clas
587602 * @throws NullPointerException if the className is null
588603 * @throws ClassNotFoundException if the class is not found
589604 * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
605+ * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
590606 * @see Class#forName(String, boolean, ClassLoader)
591607 * @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>
592608 * @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,6 +623,7 @@ public static Class<?> getClass(final String className) throws ClassNotFoundExce
607623 * @throws NullPointerException if the className is null.
608624 * @throws ClassNotFoundException if the class is not found.
609625 * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
626+ * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
610627 * @see Class#forName(String, boolean, ClassLoader)
611628 * @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>
612629 * @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>
@@ -1519,32 +1536,60 @@ public static Class<?> primitiveToWrapper(final Class<?> cls) {
15191536 }
15201537
15211538 /**
1522- * Converts a class name to a JLS style class name.
1539+ * Converts and cleans up a class name to a JLS style class name.
15231540 *
15241541 * @param className the class name.
15251542 * @return the converted name.
1526- * @throws NullPointerException if the className is null.
1543+ * @throws NullPointerException if the className is null.
15271544 * @throws IllegalArgumentException Thrown if the class name represents an array with more dimensions than the JVM supports, 255.
1545+ * @throws IllegalArgumentException Thrown if the class name length is greater than 65,535.
1546+ * @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
1547+ * CONSTANT_Class_info</a>
1548+ * @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>
1549+ * @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>
15281550 */
1529- private static String toCanonicalName (final String className ) {
1551+ private static String toCleanName (final String className ) {
15301552 String canonicalName = StringUtils .deleteWhitespace (className );
15311553 Objects .requireNonNull (canonicalName , "className" );
1554+ if (canonicalName .isEmpty ()) {
1555+ throw new IllegalArgumentException ("Class name is empty" );
1556+ }
1557+ final String encodedArrayOpen = "[" ;
1558+ final String encodedClassNameStart = "L" ;
1559+ final String encodedClassNameEnd = ";" ;
1560+ final boolean encodedName = canonicalName .startsWith (encodedArrayOpen ) && canonicalName .endsWith (encodedClassNameEnd );
1561+ if (encodedName ) {
1562+ final int arrIdx = canonicalName .indexOf (encodedClassNameStart );
1563+ if (arrIdx > MAX_JVM_ARRAY_DIMENSION ) {
1564+ throw new IllegalArgumentException ("Array dimension greater than JVM specification maximum of 255." );
1565+ }
1566+ if (arrIdx < 0 ) {
1567+ throw new IllegalArgumentException ("Expected 'L' after '[' for an array style string." );
1568+ }
1569+ final int cnLen = canonicalName .length () - (arrIdx + 2 ); // account for the ending ';'
1570+ if (cnLen > MAX_CLASS_NAME_LENGTH ) {
1571+ throw new IllegalArgumentException (String .format ("Class name greater than maxium length %,d" , MAX_CLASS_NAME_LENGTH ));
1572+ }
1573+ }
15321574 final String arrayMarker = "[]" ;
1533- int arrayDim = 0 ;
1575+ final int arrIdx = canonicalName .indexOf (arrayMarker );
1576+ // The class name length without array markers.
1577+ final int cnLen = arrIdx > 0 ? arrIdx : canonicalName .length ();
1578+ if (cnLen > MAX_CLASS_NAME_LENGTH && !encodedName ) {
1579+ throw new IllegalArgumentException (String .format ("Class name greater than maxium length %,d" , MAX_CLASS_NAME_LENGTH ));
1580+ }
15341581 if (canonicalName .endsWith (arrayMarker )) {
1535- final StringBuilder classNameBuffer = new StringBuilder ();
1536- while (canonicalName .endsWith (arrayMarker )) {
1537- if (++arrayDim > MAX_JVM_ARRAY_DIMENSION ) {
1538- throw new IllegalArgumentException ("Array dimension greater than JVM specification maximum of 255." );
1539- }
1540- canonicalName = canonicalName .substring (0 , canonicalName .length () - 2 );
1541- classNameBuffer .append ("[" );
1582+ final int dims = (canonicalName .length () - arrIdx ) / 2 ;
1583+ if (dims > MAX_JVM_ARRAY_DIMENSION ) {
1584+ throw new IllegalArgumentException ("Array dimension greater than JVM specification maximum of 255." );
15421585 }
1586+ final StringBuilder classNameBuffer = new StringBuilder (StringUtils .repeat (encodedArrayOpen , dims ));
1587+ canonicalName = canonicalName .substring (0 , arrIdx );
15431588 final String abbreviation = ABBREVIATION_MAP .get (canonicalName );
15441589 if (abbreviation != null ) {
15451590 classNameBuffer .append (abbreviation );
15461591 } else {
1547- classNameBuffer .append ("L" ).append (canonicalName ).append (";" );
1592+ classNameBuffer .append (encodedClassNameStart ).append (canonicalName ).append (encodedClassNameEnd );
15481593 }
15491594 canonicalName = classNameBuffer .toString ();
15501595 }
0 commit comments