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

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

commit 8b126aa9c30c16ea05ddcc79d9851ae3d64fb4fe
Author: Gary Gregory <[email protected]>
AuthorDate: Sat Oct 17 15:15:07 2026 -0400

    Fix resource paths for unnamed packages (#1824)
---
 src/changes/changes.xml                            |  1 +
 .../org/apache/commons/lang3/ClassPathUtils.java   | 65 +++++++++++++++++-----
 2 files changed, 52 insertions(+), 14 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ef813c0b5..69da672a2 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -64,6 +64,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="fix" dev="ggregory" due-to="alhuda, Gary 
Gregory">Treat an unmatched quote as closed in ExtendedMessageFormat 
(#1819).</action>
     <action                   type="fix" dev="ggregory" due-to="gaurav kumar 
pandey, Gary Gregory">Prevent StringUtils padding from splitting surrogate 
pairs (#1813).</action>
     <action issue="LANG-441"  type="fix" dev="ggregory" due-to="Sebb, Valentin 
Rocher, Adarsh Kumar Omrey, Gary Gregory">DateUtils Javadoc does not fully 
define round() behavior.</action>
+    <action issue="LANG-1838" type="fix" dev="ggregory" due-to="Aryan R. 
Ganjoo, Gary Gregory">Fix resource paths for unnamed packages (#1824).</action>
     <!-- ADD -->
     <!-- UPDATE -->
     <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
org.apache.commons:commons-parent from 105 to 106.</action>
diff --git a/src/main/java/org/apache/commons/lang3/ClassPathUtils.java 
b/src/main/java/org/apache/commons/lang3/ClassPathUtils.java
index 4f380e3ba..052b22739 100644
--- a/src/main/java/org/apache/commons/lang3/ClassPathUtils.java
+++ b/src/main/java/org/apache/commons/lang3/ClassPathUtils.java
@@ -30,6 +30,25 @@
 //@Immutable
 public class ClassPathUtils {
 
+    /**
+     * Gets the package name of the context class.
+     * <p>
+     * Returns an empty string for an unnamed package, including when {@link 
Class#getPackage()} returns {@code null} on Java 8.
+     * </p>
+     *
+     * @param context The context class, must not be {@code null}.
+     * @return The package name, or an empty string for an unnamed package.
+     * @throws NullPointerException Thrown if {@code context} is null, 
represents an array, a primitive type or {@code void}, or represents a named 
class whose
+     *                              package is unavailable.
+     */
+    private static String getPackageName(final Class<?> context) {
+        final Package contextPackage = context.getPackage();
+        if (contextPackage == null && !context.isArray() && 
!context.isPrimitive() && context.getName().indexOf('.') < 0) {
+            return StringUtils.EMPTY;
+        }
+        return Objects.requireNonNull(contextPackage, "context").getName();
+    }
+
     /**
      * Converts a package name to a Java path ('/').
      *
@@ -54,6 +73,22 @@ public static String pathToPackage(final String path) {
         return Objects.requireNonNull(path, "path").replace('/', '.');
     }
 
+    /**
+     * Qualifies a resource name with a prefix and separator.
+     * <p>
+     * Returns {@code resourceName} unchanged when the prefix is empty.
+     * </p>
+     *
+     * @param prefix       The package name or path, must not be {@code null}.
+     * @param resourceName The resource name, validated as non-null by the 
caller.
+     * @param separator    The separator between the prefix and resource name.
+     * @return The qualified resource name, or the unchanged resource name 
when the prefix is empty.
+     * @throws NullPointerException Thrown if {@code prefix} is null.
+     */
+    private static String qualify(final String prefix, final String 
resourceName, final char separator) {
+        return prefix.isEmpty() ? resourceName : prefix + separator + 
resourceName;
+    }
+
     /**
      * Returns the fully qualified name for the resource with name {@code 
resourceName} relative to the given context.
      *
@@ -61,7 +96,9 @@ public static String pathToPackage(final String path) {
      * Note that this method does not check whether the resource actually 
exists. It only constructs the name. Null inputs are not allowed.
      * </p>
      *
-     * <p>If the context class is in an unnamed package, returns {@code 
resourceName} unchanged.</p>
+     * <p>
+     * If the context class is in an unnamed package, returns {@code 
resourceName} unchanged.
+     * </p>
      *
      * <pre>
      * ClassPathUtils.toFullyQualifiedName(StringUtils.class, 
"StringUtils.properties") = "org.apache.commons.lang3.StringUtils.properties"
@@ -76,9 +113,7 @@ public static String pathToPackage(final String path) {
     public static String toFullyQualifiedName(final Class<?> context, final 
String resourceName) {
         Objects.requireNonNull(context, "context");
         Objects.requireNonNull(resourceName, "resourceName");
-        final Package contextPackage = context.getPackage();
-        return contextPackage == null && !context.isArray() && 
!context.isPrimitive() && context.getName().indexOf('.') < 0
-                ? resourceName : 
toFullyQualifiedName(Objects.requireNonNull(contextPackage, "context"), 
resourceName);
+        return qualify(getPackageName(context), resourceName, '.');
     }
 
     /**
@@ -88,7 +123,9 @@ public static String toFullyQualifiedName(final Class<?> 
context, final String r
      * Note that this method does not check whether the resource actually 
exists. It only constructs the name. Null inputs are not allowed.
      * </p>
      *
-     * <p>If the context is an unnamed package, returns {@code resourceName} 
unchanged.</p>
+     * <p>
+     * If the context is an unnamed package, returns {@code resourceName} 
unchanged.
+     * </p>
      *
      * <pre>
      * ClassPathUtils.toFullyQualifiedName(StringUtils.class.getPackage(), 
"StringUtils.properties") = "org.apache.commons.lang3.StringUtils.properties"
@@ -102,8 +139,7 @@ public static String toFullyQualifiedName(final Class<?> 
context, final String r
     public static String toFullyQualifiedName(final Package context, final 
String resourceName) {
         Objects.requireNonNull(context, "context");
         Objects.requireNonNull(resourceName, "resourceName");
-        final String packageName = context.getName();
-        return packageName.isEmpty() ? resourceName : packageName + "." + 
resourceName;
+        return qualify(context.getName(), resourceName, '.');
     }
 
     /**
@@ -113,7 +149,9 @@ public static String toFullyQualifiedName(final Package 
context, final String re
      * Note that this method does not check whether the resource actually 
exists. It only constructs the path. Null inputs are not allowed.
      * </p>
      *
-     * <p>If the context class is in an unnamed package, returns {@code 
resourceName} unchanged.</p>
+     * <p>
+     * If the context class is in an unnamed package, returns {@code 
resourceName} unchanged.
+     * </p>
      *
      * <pre>
      * ClassPathUtils.toFullyQualifiedPath(StringUtils.class, 
"StringUtils.properties") = "org/apache/commons/lang3/StringUtils.properties"
@@ -128,9 +166,7 @@ public static String toFullyQualifiedName(final Package 
context, final String re
     public static String toFullyQualifiedPath(final Class<?> context, final 
String resourceName) {
         Objects.requireNonNull(context, "context");
         Objects.requireNonNull(resourceName, "resourceName");
-        final Package contextPackage = context.getPackage();
-        return contextPackage == null && !context.isArray() && 
!context.isPrimitive() && context.getName().indexOf('.') < 0
-                ? resourceName : 
toFullyQualifiedPath(Objects.requireNonNull(contextPackage, "context"), 
resourceName);
+        return qualify(packageToPath(getPackageName(context)), resourceName, 
'/');
     }
 
     /**
@@ -140,7 +176,9 @@ public static String toFullyQualifiedPath(final Class<?> 
context, final String r
      * Note that this method does not check whether the resource actually 
exists. It only constructs the path. Null inputs are not allowed.
      * </p>
      *
-     * <p>If the context is an unnamed package, returns {@code resourceName} 
unchanged.</p>
+     * <p>
+     * If the context is an unnamed package, returns {@code resourceName} 
unchanged.
+     * </p>
      *
      * <pre>
      * ClassPathUtils.toFullyQualifiedPath(StringUtils.class.getPackage(), 
"StringUtils.properties") = "org/apache/commons/lang3/StringUtils.properties"
@@ -154,8 +192,7 @@ public static String toFullyQualifiedPath(final Class<?> 
context, final String r
     public static String toFullyQualifiedPath(final Package context, final 
String resourceName) {
         Objects.requireNonNull(context, "context");
         Objects.requireNonNull(resourceName, "resourceName");
-        final String packageName = context.getName();
-        return packageName.isEmpty() ? resourceName : 
packageToPath(packageName) + "/" + resourceName;
+        return qualify(packageToPath(context.getName()), resourceName, '/');
     }
 
     /**

Reply via email to