[
https://issues.apache.org/jira/browse/LANG-1818?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Gary D. Gregory resolved LANG-1818.
-----------------------------------
Fix Version/s: 3.21.0
Resolution: Fixed
Thank you [~iponomarev]
PR merged, 3.21.0-SNAPSHOT build deployed.
> ClassUtils.getShortClassName(Class) misinterprets '$' in legitimate class
> names
> -------------------------------------------------------------------------------
>
> Key: LANG-1818
> URL: https://issues.apache.org/jira/browse/LANG-1818
> Project: Commons Lang
> Issue Type: Improvement
> Reporter: Ivan Ponomarev
> Priority: Minor
> Fix For: 3.21.0
>
>
> h3.Description
> {{ClassUtils.getShortClassName(String)}} operates on JVM binary names and
> necessarily relies on heuristics. As documented in its Javadoc, it cannot
> reliably distinguish package names, outer classes, and inner classes in all
> cases when given only a {{String}}.
> However, these limitations should not apply to {{getShortClassName(Class)}}
> (and {{getShortClassName(Object))}}, which currently delegate to the
> string-based method:
> {code:java}
> public static String getShortClassName(final Class<?> cls) {
> if (cls == null) {
> return StringUtils.EMPTY;
> }
> return getShortClassName(cls.getName());
> }
> {code}
> When a {{Class<?>}} instance is available, we have unambiguous metadata.
> Delegating to the string-based implementation causes legitimate $ characters
> in class identifiers to be incorrectly interpreted as inner-class separators.
> h3.Motivation
> Although uncommon, {{$}} is a valid character in Java identifiers and is used
> in practice (e.g. generated code, DSL-heavy libraries, test fixtures,
> Selenide library exports {{$}} and {{$$}} names). Treating every $ as an
> inner-class separator leads to incorrect results even for top-level and
> member classes.
> While this ambiguity is unavoidable for {{getShortClassName(String)}}, it is
> avoidable for {{getShortClassName(Class)}}.
> h3.Reproducer
> {code:java}
> class $trange {}
> class Pa$$word {}
> class ClassUtilsShortClassNameTest {
> class $Inner {}
> class Inner {
> class Ne$ted {}
> }
> @Test
> void testDollarSignImmediatelyAfterPackage() {
> assertEquals("$trange", ClassUtils.getShortClassName($trange.class));
> // Actual (before fix): ".trange"
> }
> @Test
> void testDollarSignWithinName() {
> assertEquals("Pa$$word",
> ClassUtils.getShortClassName(Pa$$word.class));
> // Actual (before fix): "Pa..word"
> }
> @Test
> void testMultipleDollarSigns() {
> assertEquals(
> getClass().getSimpleName() + ".$Inner",
> ClassUtils.getShortClassName($Inner.class)
> );
> // Actual (before fix): "ClassUtilsShortClassNameTest..Inner"
> }
> @Test
> void testNe$tedClassName() {
> assertEquals(
> getClass().getSimpleName() + ".Inner.Ne$ted",
> ClassUtils.getShortClassName(Inner.Ne$ted.class)
> );
> // Actual (before fix): "ClassUtilsShortClassNameTest.Inner.Ne.ted"
> }
> }
> {code}
> h3.Proposed fix
> * Reimplement {{getShortClassName(Class)}} using {{Class}} metadata
> ({{getSimpleName()}}, {{getDeclaringClass()}}, {{getEnclosingClass()}}, array
> component handling) instead of delegating to the string-based method.
> * Preserve existing behavior for local and anonymous classes
> (compiler-generated ordinal names) by falling back to the legacy string-based
> logic, ensuring backward compatibility with existing tests.
> * Leave {{getShortClassName(String)}} unchanged and continue to document its
> inherent ambiguity.
> As a result: {{getShortCanonicalName(String)}} continues to rely on the
> string-based implementation and remains subject to the documented
> limitations; this is expected and unchanged. This change improves correctness
> for the Class-based APIs without altering behavior for purely string-based
> usage.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)