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


The following commit(s) were added to refs/heads/master by this push:
     new 1d6ef29ce Fix CharRange.contains for negated argument ranges (#1775)
1d6ef29ce is described below

commit 1d6ef29ce004309e50bd91e27f0f5e80e1a57a76
Author: alhuda <[email protected]>
AuthorDate: Mon Aug 17 07:32:28 2026 +0530

    Fix CharRange.contains for negated argument ranges (#1775)
---
 .../java/org/apache/commons/lang3/CharRange.java   | 12 ++++++++++
 .../org/apache/commons/lang3/CharRangeTest.java    | 27 ++++++++++++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/src/main/java/org/apache/commons/lang3/CharRange.java 
b/src/main/java/org/apache/commons/lang3/CharRange.java
index cca54206d..b76276631 100644
--- a/src/main/java/org/apache/commons/lang3/CharRange.java
+++ b/src/main/java/org/apache/commons/lang3/CharRange.java
@@ -269,6 +269,18 @@ public boolean contains(final CharRange range) {
             return range.end < start || range.start > end;
         }
         if (range.negated) {
+            // range denotes [0, range.start - 1] union [range.end + 1, 
Character.MAX_VALUE]
+            final boolean lowEmpty = range.start == 0;
+            final boolean highEmpty = range.end == Character.MAX_VALUE;
+            if (lowEmpty && highEmpty) {
+                return true; // range denotes the empty set
+            }
+            if (lowEmpty) {
+                return end == Character.MAX_VALUE && start <= range.end + 1;
+            }
+            if (highEmpty) {
+                return start == 0 && end + 1 >= range.start;
+            }
             return start == 0 && end == Character.MAX_VALUE;
         }
         return start <= range.start && end >= range.end;
diff --git a/src/test/java/org/apache/commons/lang3/CharRangeTest.java 
b/src/test/java/org/apache/commons/lang3/CharRangeTest.java
index becf3cf3b..c401c7693 100644
--- a/src/test/java/org/apache/commons/lang3/CharRangeTest.java
+++ b/src/test/java/org/apache/commons/lang3/CharRangeTest.java
@@ -260,6 +260,33 @@ void testContains_Charrange() {
         assertTrue(notbd.contains(notae));
     }
 
+    @Test
+    void testContains_Charrange_negatedArgumentTouchingBounds() {
+        // A negated argument denotes [0, start - 1] union [end + 1, 
MAX_VALUE]. When its excluded range touches 0 or
+        // MAX_VALUE that set collapses to the empty set or a single 
contiguous interval, so a non-full range can contain it.
+        final CharRange emptySet = CharRange.isNotIn((char) 0, 
Character.MAX_VALUE);
+        assertTrue(CharRange.is('x').contains(emptySet));
+        assertTrue(CharRange.isIn((char) 0, 
Character.MAX_VALUE).contains(emptySet));
+
+        // isNotIn(0, 'a' - 1) denotes ['a', MAX_VALUE]
+        final CharRange fromA = CharRange.isNotIn((char) 0, (char) ('a' - 1));
+        assertTrue(CharRange.isIn('a', Character.MAX_VALUE).contains(fromA));
+        assertTrue(CharRange.isIn('Z', Character.MAX_VALUE).contains(fromA));
+        assertFalse(CharRange.isIn('b', Character.MAX_VALUE).contains(fromA));
+        assertFalse(CharRange.isIn('a', 'z').contains(fromA));
+
+        // isNotIn('b', MAX_VALUE) denotes [0, 'a']
+        final CharRange upToA = CharRange.isNotIn('b', Character.MAX_VALUE);
+        assertTrue(CharRange.isIn((char) 0, 'a').contains(upToA));
+        assertTrue(CharRange.isIn((char) 0, 'b').contains(upToA));
+        assertFalse(CharRange.isIn((char) 1, 'a').contains(upToA));
+        assertFalse(CharRange.isIn((char) 0, 'Z').contains(upToA));
+
+        // A fully interior excluded range is still only contained by the full 
range.
+        assertFalse(CharRange.is('c').contains(CharRange.isNot('c')));
+        assertTrue(CharRange.isIn((char) 0, 
Character.MAX_VALUE).contains(CharRange.isNot('c')));
+    }
+
     @Test
     void testContainsNullArg() {
         final CharRange range = CharRange.is('a');

Reply via email to