From b686df82a7f66eaa195bf4495f8472ed561641d5 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 27 Jan 2026 18:16:49 +0000 Subject: [PATCH 1/3] fix lang-1818 --- .../org/apache/commons/lang3/ClassUtils.java | 39 +++++++--- .../lang3/ClassUtilsShortClassNameTest.java | 76 +++++++++++++++++++ 2 files changed, 104 insertions(+), 11 deletions(-) create mode 100644 src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java b/src/main/java/org/apache/commons/lang3/ClassUtils.java index 5c2cf099d19..42a2443521f 100644 --- a/src/main/java/org/apache/commons/lang3/ClassUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassUtils.java @@ -18,9 +18,11 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -1011,11 +1013,6 @@ public static String getShortCanonicalName(final String canonicalName) { /** * Gets the class name minus the package name from a {@link Class}. * - *

- * This method simply gets the name using {@code Class.getName()} and then calls {@link #getShortClassName(String)}. See - * relevant notes there. - *

- * * @param cls the class to get the short name for. * @return the class name without the package name or an empty string. If the class is an inner class then the returned * value will contain the outer class or classes separated with {@code .} (dot) character. @@ -1024,17 +1021,37 @@ public static String getShortClassName(final Class cls) { if (cls == null) { return StringUtils.EMPTY; } - return getShortClassName(cls.getName()); + int dim = 0; + Class c = cls; + while (c.isArray()) { + dim++; + c = c.getComponentType(); + } + + final String base; + // Preserve legacy behavior for anonymous/local classes (keeps compiler ordinals: $13, $10Named, etc.) + if (c.isAnonymousClass() || c.isLocalClass()) { + base = getShortClassName(c.getName()); + } else { + final Deque parts = new ArrayDeque<>(); + Class x = c; + while (x != null) { + parts.push(x.getSimpleName()); + x = x.getDeclaringClass(); + } + base = String.join(String.valueOf(PACKAGE_SEPARATOR_CHAR), parts); + } + + final StringBuilder sb = new StringBuilder(base); + for (int i = 0; i < dim; i++) { + sb.append("[]"); + } + return sb.toString(); } /** * Gets the class name of the {@code object} without the package name or names. * - *

- * The method looks up the class of the object and then converts the name of the class invoking - * {@link #getShortClassName(Class)} (see relevant notes there). - *

- * * @param object the class to get the short name for, may be {@code null}. * @param valueIfNull the value to return if the object is {@code null}. * @return the class name of the object without the package name, or {@code valueIfNull} if the argument {@code object} diff --git a/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java b/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java new file mode 100644 index 00000000000..026b499dfc5 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class $trange { + +} + +class Pa$$word { + +} + +/** + * Tests for https://issues.apache.org/jira/browse/LANG-1818 + */ +public class ClassUtilsShortClassNameTest { + + class $Inner { + + } + + class Inner { + class Ne$ted { + + } + } + + @Test + void testDollarSignImmediatelyAfterPackage() { + String result = ClassUtils.getShortClassName($trange.class); + assertEquals("$trange", result); + } + + @Test + void testDollarSignWithinName() { + String result = ClassUtils.getShortClassName(Pa$$word.class); + assertEquals("Pa$$word", result); + } + + @Test + void testMultipleDollarSigns() { + String result = ClassUtils.getShortClassName($Inner.class); + assertEquals(getClass().getSimpleName() + ".$Inner", result); + } + + @Test + void testInnerClassName() { + String result = ClassUtils.getShortClassName(Inner.class); + assertEquals(getClass().getSimpleName() + ".Inner", result); + } + + @Test + void testNe$tedClassName() { + String result = ClassUtils.getShortClassName(Inner.Ne$ted.class); + assertEquals(getClass().getSimpleName() + ".Inner.Ne$ted", result); + } +} From ada66f950f2e40695d8856b38a766f2eeced62f3 Mon Sep 17 00:00:00 2001 From: Ivan Date: Wed, 28 Jan 2026 15:47:35 +0000 Subject: [PATCH 2/3] fix review comments --- .../org/apache/commons/lang3/ClassUtils.java | 10 ++-------- .../lang3/ClassUtilsShortClassNameTest.java | 20 +++++++++---------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java b/src/main/java/org/apache/commons/lang3/ClassUtils.java index 42a2443521f..3032caee6d0 100644 --- a/src/main/java/org/apache/commons/lang3/ClassUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassUtils.java @@ -1027,7 +1027,6 @@ public static String getShortClassName(final Class cls) { dim++; c = c.getComponentType(); } - final String base; // Preserve legacy behavior for anonymous/local classes (keeps compiler ordinals: $13, $10Named, etc.) if (c.isAnonymousClass() || c.isLocalClass()) { @@ -1039,14 +1038,9 @@ public static String getShortClassName(final Class cls) { parts.push(x.getSimpleName()); x = x.getDeclaringClass(); } - base = String.join(String.valueOf(PACKAGE_SEPARATOR_CHAR), parts); - } - - final StringBuilder sb = new StringBuilder(base); - for (int i = 0; i < dim; i++) { - sb.append("[]"); + base = String.join(".", parts); } - return sb.toString(); + return base + StringUtils.repeat("[]", dim); } /** diff --git a/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java b/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java index 026b499dfc5..e8f47769aa3 100644 --- a/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java +++ b/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java @@ -30,7 +30,7 @@ class Pa$$word { } /** - * Tests for https://issues.apache.org/jira/browse/LANG-1818 + * Tests for LANG-1818 */ public class ClassUtilsShortClassNameTest { @@ -46,31 +46,29 @@ class Ne$ted { @Test void testDollarSignImmediatelyAfterPackage() { - String result = ClassUtils.getShortClassName($trange.class); - assertEquals("$trange", result); + assertEquals("$trange", ClassUtils.getShortClassName($trange.class)); } @Test void testDollarSignWithinName() { - String result = ClassUtils.getShortClassName(Pa$$word.class); - assertEquals("Pa$$word", result); + assertEquals("Pa$$word", ClassUtils.getShortClassName(Pa$$word.class)); } @Test void testMultipleDollarSigns() { - String result = ClassUtils.getShortClassName($Inner.class); - assertEquals(getClass().getSimpleName() + ".$Inner", result); + assertEquals(getClass().getSimpleName() + ".$Inner", + ClassUtils.getShortClassName($Inner.class)); } @Test void testInnerClassName() { - String result = ClassUtils.getShortClassName(Inner.class); - assertEquals(getClass().getSimpleName() + ".Inner", result); + assertEquals(getClass().getSimpleName() + ".Inner", + ClassUtils.getShortClassName(Inner.class)); } @Test void testNe$tedClassName() { - String result = ClassUtils.getShortClassName(Inner.Ne$ted.class); - assertEquals(getClass().getSimpleName() + ".Inner.Ne$ted", result); + assertEquals(getClass().getSimpleName() + ".Inner.Ne$ted", + ClassUtils.getShortClassName(Inner.Ne$ted.class)); } } From 75de25ead6212fbc7ffd901140a2732d50e5a00d Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 28 Jan 2026 15:13:23 -0500 Subject: [PATCH 3/3] Checkstyle --- .../apache/commons/lang3/ClassUtilsShortClassNameTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java b/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java index e8f47769aa3..d19bc039306 100644 --- a/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java +++ b/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java @@ -17,10 +17,10 @@ package org.apache.commons.lang3; -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + class $trange { }