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

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git

commit 4d6ccd07522d97b2d29376ad76efd916d756fb41
Author: James Bognar <[email protected]>
AuthorDate: Fri Jul 17 12:56:34 2026 -0400

    TODO-238 — BUG × juneau-commons fixes (BUG-03 LocalDir hashCode, BUG-07 
locale-safe casing, BUG-09 defensive copies, BUG-15 null guards, BUG-16 holder 
null-coerce) + make Version equals/hashCode/compareTo strict (prefix-matching 
moved to matches()). Breaking change (10.0.0).
    
    Co-authored-by: Cursor <[email protected]>
---
 .../juneau/commons/AppliedAnnotationObject.java    |  2 +-
 .../commons/AppliedOnClassAnnotationObject.java    |  2 +-
 .../juneau/commons/bean/BeanCtorAnnotation.java    |  2 +-
 .../juneau/commons/bean/BeanIgnoreAnnotation.java  |  2 +-
 .../juneau/commons/bean/BeanPropAnnotation.java    |  4 +-
 .../juneau/commons/bean/BeanTypeAnnotation.java    |  2 +-
 .../apache/juneau/commons/http/HeaderElement.java  |  2 +-
 .../juneau/commons/inject/BasicBeanStore.java      |  7 +++
 .../juneau/commons/inject/BeanAnnotation.java      |  4 +-
 .../org/apache/juneau/commons/io/LocalDir.java     |  2 +-
 .../org/apache/juneau/commons/lang/ByteHolder.java | 27 +++++++----
 .../apache/juneau/commons/lang/IntegerHolder.java  | 27 +++++++----
 .../org/apache/juneau/commons/lang/Version.java    | 56 ++++++++++++++--------
 .../apache/juneau/commons/utils/ClassUtils.java    |  2 +
 .../apache/juneau/commons/utils/StringUtils.java   | 15 +++---
 .../apache/juneau/commons/lang/Version_Test.java   | 33 +++++++++----
 .../juneau/commons/utils/ClassUtils_Test.java      |  2 +-
 17 files changed, 125 insertions(+), 66 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/AppliedAnnotationObject.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/AppliedAnnotationObject.java
index e8ea00ea5f..78c44d49c8 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/AppliedAnnotationObject.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/AppliedAnnotationObject.java
@@ -713,6 +713,6 @@ public class AppliedAnnotationObject extends 
AnnotationObject {
         * @return The targets this annotation applies to.
         */
        public String[] on() {
-               return on;
+               return copyOf(on);
        }
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/AppliedOnClassAnnotationObject.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/AppliedOnClassAnnotationObject.java
index 03591f7944..30498dee46 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/AppliedOnClassAnnotationObject.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/AppliedOnClassAnnotationObject.java
@@ -108,6 +108,6 @@ public class AppliedOnClassAnnotationObject extends 
AppliedAnnotationObject {
         * @return The targets this annotation applies to.
         */
        public Class<?>[] onClass() {
-               return onClass;
+               return copyOf(onClass);
        }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanCtorAnnotation.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanCtorAnnotation.java
index 93fbdeaf22..3d3f032e73 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanCtorAnnotation.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanCtorAnnotation.java
@@ -104,7 +104,7 @@ public class BeanCtorAnnotation {
 
                @Override /* Overridden from BeanCtor */
                public String[] description() {
-                       return description;
+                       return copyOf(description);
                }
 
                @Override /* Overridden from BeanCtor */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanIgnoreAnnotation.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanIgnoreAnnotation.java
index 4e23994a1a..3b7e675e4b 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanIgnoreAnnotation.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanIgnoreAnnotation.java
@@ -104,7 +104,7 @@ public class BeanIgnoreAnnotation {
 
                @Override /* Overridden from BeanIgnore */
                public String[] description() {
-                       return description;
+                       return copyOf(description);
                }
 
                @Override /* Overridden from BeanIgnore */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanPropAnnotation.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanPropAnnotation.java
index aa8b0fad9b..da5f7d5971 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanPropAnnotation.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanPropAnnotation.java
@@ -228,7 +228,7 @@ public class BeanPropAnnotation {
 
                @Override /* Overridden from BeanProp */
                public String[] description() {
-                       return description;
+                       return copyOf(description);
                }
 
                @Override /* Overridden from BeanProp */
@@ -248,7 +248,7 @@ public class BeanPropAnnotation {
 
                @Override /* Overridden from BeanProp */
                public Class<?>[] params() {
-                       return params;
+                       return copyOf(params);
                }
 
                @Override /* Overridden from BeanProp */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanTypeAnnotation.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanTypeAnnotation.java
index f1e38ca8ae..91b6b13e22 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanTypeAnnotation.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/BeanTypeAnnotation.java
@@ -296,7 +296,7 @@ public class BeanTypeAnnotation {
 
                @Override /* Overridden from BeanType */
                public String[] description() {
-                       return description;
+                       return copyOf(description);
                }
 
                @Override /* Overridden from BeanType */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/http/HeaderElement.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/http/HeaderElement.java
index 9046dab64e..4536ea21a5 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/http/HeaderElement.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/http/HeaderElement.java
@@ -55,6 +55,6 @@ public class HeaderElement {
         * @return The parameters array, never <jk>null</jk>.
         */
        public NameValuePair[] getParameters() {
-               return parameters;
+               return parameters.clone();
        }
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
index aafd45c5f2..1077777d57 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
@@ -17,6 +17,7 @@
 package org.apache.juneau.commons.inject;
 
 import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
 import static org.apache.juneau.commons.utils.CollectionUtils.*;
 import static org.apache.juneau.commons.utils.Shorts.*;
 import static org.apache.juneau.commons.utils.StringUtils.*;
@@ -97,6 +98,10 @@ public class BasicBeanStore implements WritableBeanStore {
        private static final String PROP_parent = "parent";
        private static final String PROP_type = "type";
 
+       // Argument name constants for assertArgNotNull
+       private static final String ARG_beanType = "beanType";
+       private static final String ARG_onClassOrObject = "onClassOrObject";
+
        private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, 
Supplier<?>>> entries;
        private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, 
Supplier<?>>> defaults;
        private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, 
BeanSourceMeta>> entryMetadata;
@@ -704,6 +709,8 @@ public class BasicBeanStore implements WritableBeanStore {
         */
        @Override
        public <T> Optional<T> createBeanFromMethod(Class<T> beanType, Object 
onClassOrObject, Predicate<MethodInfo> filter, Object... extraBeans) {
+               assertArgNotNull(ARG_beanType, beanType);
+               assertArgNotNull(ARG_onClassOrObject, onClassOrObject);
                Object resource = onClassOrObject instanceof Class ? null : 
onClassOrObject;
                Class<?> resourceClass = onClassOrObject instanceof Class<?> c 
? c : onClassOrObject.getClass();
                return info(resourceClass)
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanAnnotation.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanAnnotation.java
index 39f1919e82..12562d535d 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanAnnotation.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanAnnotation.java
@@ -173,7 +173,7 @@ public class BeanAnnotation {
 
                @Override /* Overridden from Bean */
                public String[] methodScope() {
-                       return methodScope;
+                       return copyOf(methodScope);
                }
 
                @Override /* Overridden from Bean */
@@ -188,7 +188,7 @@ public class BeanAnnotation {
 
                @Override /* Overridden from annotation */
                public String[] description() {
-                       return description;
+                       return copyOf(description);
                }
 
                @Override /* Overridden from Bean */
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/LocalDir.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/LocalDir.java
index e33719a9d2..a9fd3b3e7d 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/LocalDir.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/io/LocalDir.java
@@ -151,7 +151,7 @@ public class LocalDir {
                this.clazz = assertArgNotNull(ARG_clazz, clazz);
                this.clazzPath = "/".equals(clazzPath) ? "/" : 
StringUtils.nullIfEmpty(trimTrailingSlashes(clazzPath));
                this.path = null;
-               this.hashCode = h(clazz, clazzPath);
+               this.hashCode = h(clazz, this.clazzPath);
        }
 
        /**
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/ByteHolder.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/ByteHolder.java
index f6348abd55..723ee68f0f 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/ByteHolder.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/ByteHolder.java
@@ -121,7 +121,8 @@ public class ByteHolder extends Holder<Byte> {
         * @return This object.
         */
        public ByteHolder add(Byte x) {
-               set((byte)(get() + (x == null ? 0 : x)));
+               var v = get();
+               set((byte)((v == null ? 0 : v) + (x == null ? 0 : x)));
                return this;
        }
 
@@ -139,8 +140,10 @@ public class ByteHolder extends Holder<Byte> {
         * @return The new value after addition.
         */
        public Byte addAndGet(Byte x) {
-               set((byte)(get() + (x == null ? 0 : x)));
-               return get();
+               var v = get();
+               var result = (byte)((v == null ? 0 : v) + (x == null ? 0 : x));
+               set(result);
+               return result;
        }
 
        /**
@@ -156,7 +159,8 @@ public class ByteHolder extends Holder<Byte> {
         * @return This object.
         */
        public ByteHolder decrement() {
-               set((byte)(get() - 1));
+               var v = get();
+               set((byte)((v == null ? 0 : v) - 1));
                return this;
        }
 
@@ -173,8 +177,10 @@ public class ByteHolder extends Holder<Byte> {
         * @return The decremented value.
         */
        public Byte decrementAndGet() {
-               set((byte)(get() - 1));
-               return get();
+               var v = get();
+               var result = (byte)((v == null ? 0 : v) - 1);
+               set(result);
+               return result;
        }
 
        /**
@@ -190,7 +196,8 @@ public class ByteHolder extends Holder<Byte> {
         * @return This object.
         */
        public ByteHolder increment() {
-               set((byte)(get() + 1));
+               var v = get();
+               set((byte)((v == null ? 0 : v) + 1));
                return this;
        }
 
@@ -207,8 +214,10 @@ public class ByteHolder extends Holder<Byte> {
         * @return The incremented value.
         */
        public Byte incrementAndGet() {
-               set((byte)(get() + 1));
-               return get();
+               var v = get();
+               var result = (byte)((v == null ? 0 : v) + 1);
+               set(result);
+               return result;
        }
 
        /**
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/IntegerHolder.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/IntegerHolder.java
index 57c8b6d0c1..1e72063585 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/IntegerHolder.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/IntegerHolder.java
@@ -121,7 +121,8 @@ public class IntegerHolder extends Holder<Integer> {
         * @return This object.
         */
        public IntegerHolder add(Integer x) {
-               set(get() + (x == null ? 0 : x));
+               var v = get();
+               set((v == null ? 0 : v) + (x == null ? 0 : x));
                return this;
        }
 
@@ -139,8 +140,10 @@ public class IntegerHolder extends Holder<Integer> {
         * @return The new value after addition.
         */
        public Integer addAndGet(Integer x) {
-               set(get() + (x == null ? 0 : x));
-               return get();
+               var v = get();
+               var result = (v == null ? 0 : v) + (x == null ? 0 : x);
+               set(result);
+               return result;
        }
 
        /**
@@ -156,7 +159,8 @@ public class IntegerHolder extends Holder<Integer> {
         * @return This object.
         */
        public IntegerHolder decrement() {
-               set(get() - 1);
+               var v = get();
+               set((v == null ? 0 : v) - 1);
                return this;
        }
 
@@ -173,8 +177,10 @@ public class IntegerHolder extends Holder<Integer> {
         * @return The decremented value.
         */
        public Integer decrementAndGet() {
-               set(get() - 1);
-               return get();
+               var v = get();
+               var result = (v == null ? 0 : v) - 1;
+               set(result);
+               return result;
        }
 
        /**
@@ -212,7 +218,8 @@ public class IntegerHolder extends Holder<Integer> {
         * @return This object.
         */
        public IntegerHolder increment() {
-               set(get() + 1);
+               var v = get();
+               set((v == null ? 0 : v) + 1);
                return this;
        }
 
@@ -229,8 +236,10 @@ public class IntegerHolder extends Holder<Integer> {
         * @return The incremented value.
         */
        public Integer incrementAndGet() {
-               set(get() + 1);
-               return get();
+               var v = get();
+               var result = (v == null ? 0 : v) + 1;
+               set(result);
+               return result;
        }
 
        /**
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/Version.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/Version.java
index 1fec0218aa..e0dd678ac0 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/Version.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/lang/Version.java
@@ -27,6 +27,12 @@ import java.util.*;
  * <p>
  * Used to compare version numbers.
  *
+ * <h5 class='section'>Equality vs. matching:</h5>
+ * <p>
+ * {@link #equals(Object)}, {@link #hashCode()} and {@link 
#compareTo(Version)} are <b>strict</b> and
+ * mutually consistent &mdash; two versions are only equal when their full 
part sequences are identical
+ * (e.g. <js>"1.2"</js> is <b>not</b> equal to <js>"1.2.3"</js> or 
<js>"1.2.0"</js>).  For the looser
+ * prefix/compatibility comparison (where trailing parts are ignored), use 
{@link #matches(Version)}.
  */
 public class Version implements Comparable<Version> {
 
@@ -93,26 +99,7 @@ public class Version implements Comparable<Version> {
 
        @Override /* Overridden from Object */
        public boolean equals(Object o) {
-               return o instanceof Version o2 && eq(this, o2, Version::equals);
-       }
-
-       /**
-        * Returns <jk>true</jk> if the specified version is equal to this 
version.
-        *
-        * <h5 class='section'>Example:</h5>
-        * <p class='bjava'>
-        *      
<jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isEqualsTo(Version.<jsm>of</jsm>(<js>"1.2.3"</js>)));
-        *      
<jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isEqualsTo(Version.<jsm>of</jsm>(<js>"1.2"</js>)));
-        * </p>
-        *
-        * @param v The version to compare to.
-        * @return <jk>true</jk> if the specified version is equal to this 
version.
-        */
-       public boolean equals(Version v) {
-               for (int i = 0; i < Math.min(parts.length, v.parts.length); i++)
-                       if (v.parts[i] - parts[i] != 0)
-                               return false;
-               return true;
+               return o instanceof Version o2 && Arrays.equals(parts, 
o2.parts);
        }
 
        /**
@@ -248,6 +235,35 @@ public class Version implements Comparable<Version> {
                return ! exclusive;
        }
 
+       /**
+        * Returns <jk>true</jk> if this version matches the specified version, 
comparing only the parts they have in
+        * common (i.e. one version is a prefix of the other).
+        *
+        * <p>
+        * This is a looser comparison than {@link #equals(Object)}, which 
requires the full version part sequences to be
+        * identical.  Two versions <i>match</i> when every part up to the 
length of the shorter version is identical; any
+        * trailing parts on the longer version are ignored.  Unlike {@link 
#equals(Object)}, this relation is <b>not</b>
+        * consistent with {@link #hashCode()} and must never be used as the 
basis for hash- or tree-based collection
+        * membership.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      
<jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).matches(Version.<jsm>of</jsm>(<js>"1.2.3"</js>)));
+        *      
<jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).matches(Version.<jsm>of</jsm>(<js>"1.2"</js>)));
+        *      
<jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2"</js>).matches(Version.<jsm>of</jsm>(<js>"1.2.3"</js>)));
+        *      
<jsm>assertFalse</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).matches(Version.<jsm>of</jsm>(<js>"1.2.4"</js>)));
+        * </p>
+        *
+        * @param v The version to compare to.
+        * @return <jk>true</jk> if this version matches the specified version 
on their common parts.
+        */
+       public boolean matches(Version v) {
+               for (int i = 0; i < Math.min(parts.length, v.parts.length); i++)
+                       if (v.parts[i] - parts[i] != 0)
+                               return false;
+               return true;
+       }
+
        @Override /* Overridden from Object */
        public String toString() {
                return join(parts, '.');
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ClassUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ClassUtils.java
index 6a532892ef..c5e507debe 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ClassUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ClassUtils.java
@@ -48,6 +48,7 @@ public class ClassUtils {
        // Argument name constants for assertArgNotNull
        private static final String ARG_value = "value";
        private static final String ARG_args = "args";
+       private static final String ARG_paramTypes = "paramTypes";
        private static final String ARG_x = "x";
 
        /**
@@ -357,6 +358,7 @@ public class ClassUtils {
         *      matches (fast path optimization).
         */
        public static Object[] getMatchingArgs(Class<?>[] paramTypes, 
Object...args) {
+               assertArgNotNull(ARG_paramTypes, paramTypes);
                assertArgNotNull(ARG_args, args);
                var needsShuffle = paramTypes.length != args.length;
                if (! needsShuffle) {
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/StringUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/StringUtils.java
index b5a48e6549..783ef5c977 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/StringUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/StringUtils.java
@@ -516,7 +516,7 @@ public class StringUtils {
                        if (i == 0) {
                                result.append(uncapitalize(word));
                        } else {
-                               result.append(capitalize(word.toLowerCase()));
+                               result.append(capitalize(lowerCaseRoot(word)));
                        }
                }
 
@@ -2699,9 +2699,9 @@ public class StringUtils {
                if (end > lines.length)
                        end = lines.length;
                var sb = new StringBuilder();
-               var format = String.format("%%0%dd", digits);
+               var format = String.format(Locale.ROOT, "%%0%dd", digits);
                for (var l : fixedSizeList(lines).subList(start - 1, end))
-                       sb.append(String.format(format, start++)).append(": 
").append(l).append("\n");
+                       sb.append(String.format(Locale.ROOT, format, 
start++)).append(": ").append(l).append("\n");
                return sb.toString();
        }
 
@@ -3283,6 +3283,7 @@ public class StringUtils {
                "java:S3776" // Inherent branching in a hand-written 
JSON-number-grammar scanner (sign, integer part, optional fraction, optional 
exponent); splitting would harm readability.
        })
        public static int matchNumberPrefix(String s, boolean 
allowLeadingZeros) {
+               assertArgNotNull(ARG_s, s);
                var len = s.length();
                var i = 0;
                if (i < len && s.charAt(i) == '-')
@@ -4249,7 +4250,7 @@ public class StringUtils {
                for (var i = 0; i < words.size(); i++) {
                        if (i > 0)
                                result.append('-');
-                       result.append(words.get(i).toLowerCase());
+                       result.append(lowerCaseRoot(words.get(i)));
                }
 
                return result.toString();
@@ -5609,7 +5610,7 @@ public class StringUtils {
 
                var result = new StringBuilder();
                for (var word : words) {
-                       result.append(capitalize(word.toLowerCase()));
+                       result.append(capitalize(lowerCaseRoot(word)));
                }
 
                return result.toString();
@@ -5640,7 +5641,7 @@ public class StringUtils {
                if (count == 1)
                        return word;
 
-               var lower = word.toLowerCase();
+               var lower = lowerCaseRoot(word);
                var length = word.length();
 
                // Words ending in s, x, z, ch, sh -> add "es"
@@ -6380,7 +6381,7 @@ public class StringUtils {
                for (var i = 0; i < words.size(); i++) {
                        if (i > 0)
                                result.append('_');
-                       result.append(words.get(i).toLowerCase());
+                       result.append(lowerCaseRoot(words.get(i)));
                }
 
                return result.toString();
diff --git 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/lang/Version_Test.java
 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/lang/Version_Test.java
index 433b6051a5..689f9b9ff7 100644
--- 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/lang/Version_Test.java
+++ 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/lang/Version_Test.java
@@ -92,15 +92,26 @@ class Version_Test extends TestBase {
                assertTrue(x.isAtMost(of("1.3.0.1")));
        }
 
-       @Test void a04_isEqualsTo() {
+       @Test void a04_equals() {
                var x = of("1.2.3");
 
+               // equals(Object) is strict/exact: only identical part 
sequences are equal.
                assertEquals(x, of("1.2.3"));
-               assertEquals(x, of("1.2"));
-               assertEquals(x, of("1.2.3.4"));
+               assertNotEquals(x, of("1.2"));
+               assertNotEquals(x, of("1.2.3.4"));
                assertNotEquals(x, of("1.2.4"));
        }
 
+       @Test void a04b_matches() {
+               var x = of("1.2.3");
+
+               // matches(Version) is the looser prefix comparison (trailing 
parts ignored).
+               assertTrue(x.matches(of("1.2.3")));
+               assertTrue(x.matches(of("1.2")));
+               assertTrue(x.matches(of("1.2.3.4")));
+               assertFalse(x.matches(of("1.2.4")));
+       }
+
        @Test void a05_compareTo() {
                var l = l(
                        of("1.2.3"),
@@ -165,18 +176,22 @@ class Version_Test extends TestBase {
        void b06_equalsObject_versionsWithDifferentLengths() {
                var v1 = of("1.2");
                var v2 = of("1.2.0");
-               // equals(Version) compares only common parts, so these should 
be equal
-               assertEquals(v1, v2);
-               assertEquals(v2, v1);
+               // equals(Object) is strict: differing lengths are not equal 
(but they still match()).
+               assertNotEquals(v1, v2);
+               assertNotEquals(v2, v1);
+               assertTrue(v1.matches(v2));
+               assertTrue(v2.matches(v1));
        }
 
        @Test
        void b07_equalsObject_versionsWithTrailingZeros() {
                var v1 = of("1.2.3");
                var v2 = of("1.2.3.0");
-               // equals(Version) compares only common parts, so these should 
be equal
-               assertEquals(v1, v2);
-               assertEquals(v2, v1);
+               // equals(Object) is strict: a trailing zero makes them unequal 
(but they still match()).
+               assertNotEquals(v1, v2);
+               assertNotEquals(v2, v1);
+               assertTrue(v1.matches(v2));
+               assertTrue(v2.matches(v1));
        }
 
        @Test
diff --git 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/utils/ClassUtils_Test.java
 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/utils/ClassUtils_Test.java
index 1617787c4b..59c2fe1acc 100644
--- 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/utils/ClassUtils_Test.java
+++ 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/utils/ClassUtils_Test.java
@@ -303,7 +303,7 @@ class ClassUtils_Test {
                assertNull(result7[1]);
 
                // Null paramTypes - should throw
-               assertThrows(NullPointerException.class, () -> {
+               assertThrows(IllegalArgumentException.class, () -> {
                        getMatchingArgs(null, "test");
                });
        }

Reply via email to