This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new 4052e9862 LANG-1818: Fix ClassUtils.getShortClassName(Class) to 
correctly handle $ in valid class names (#1591)
4052e9862 is described below

commit 4052e9862bf540f0b294bcf58aad5b5e85eae4ad
Author: Ivan Ponomarev <[email protected]>
AuthorDate: Wed Jan 28 20:29:46 2026 +0000

    LANG-1818: Fix ClassUtils.getShortClassName(Class) to correctly handle $ in 
valid class names (#1591)
    
    * fix lang-1818
    
    * fix review comments
    
    * Checkstyle
    
    ---------
    
    Co-authored-by: Gary Gregory <[email protected]>
---
 .../java/org/apache/commons/lang3/ClassUtils.java  | 33 ++++++----
 .../lang3/ClassUtilsShortClassNameTest.java        | 74 ++++++++++++++++++++++
 2 files changed, 96 insertions(+), 11 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java 
b/src/main/java/org/apache/commons/lang3/ClassUtils.java
index 5c2cf099d..3032caee6 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}.
      *
-     * <p>
-     * This method simply gets the name using {@code Class.getName()} and then 
calls {@link #getShortClassName(String)}. See
-     * relevant notes there.
-     * </p>
-     *
      * @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,31 @@ 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<String> parts = new ArrayDeque<>();
+            Class<?> x = c;
+            while (x != null) {
+                parts.push(x.getSimpleName());
+                x = x.getDeclaringClass();
+            }
+            base = String.join(".", parts);
+        }
+        return base + StringUtils.repeat("[]", dim);
     }
 
     /**
      * Gets the class name of the {@code object} without the package name or 
names.
      *
-     * <p>
-     * 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).
-     * </p>
-     *
      * @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 000000000..d19bc0393
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/ClassUtilsShortClassNameTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class $trange {
+
+}
+
+class Pa$$word {
+
+}
+
+/**
+ * Tests for <a 
href="https://issues.apache.org/jira/browse/LANG-1818";>LANG-1818</a>
+ */
+public class ClassUtilsShortClassNameTest {
+
+    class $Inner {
+
+    }
+
+    class Inner {
+        class Ne$ted {
+
+        }
+    }
+
+    @Test
+    void testDollarSignImmediatelyAfterPackage() {
+        assertEquals("$trange", ClassUtils.getShortClassName($trange.class));
+    }
+
+    @Test
+    void testDollarSignWithinName() {
+        assertEquals("Pa$$word", ClassUtils.getShortClassName(Pa$$word.class));
+    }
+
+    @Test
+    void testMultipleDollarSigns() {
+        assertEquals(getClass().getSimpleName() + ".$Inner",
+                ClassUtils.getShortClassName($Inner.class));
+    }
+
+    @Test
+    void testInnerClassName() {
+        assertEquals(getClass().getSimpleName() + ".Inner",
+                ClassUtils.getShortClassName(Inner.class));
+    }
+
+    @Test
+    void testNe$tedClassName() {
+        assertEquals(getClass().getSimpleName() + ".Inner.Ne$ted",
+                ClassUtils.getShortClassName(Inner.Ne$ted.class));
+    }
+}

Reply via email to