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');