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 13e38e1fb Internal refactoring.
13e38e1fb is described below

commit 13e38e1fb3b37c93c2ce0497c5ab1428695ccf96
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Aug 16 23:04:37 2026 -0400

    Internal refactoring.
---
 .../java/org/apache/commons/lang3/CharRange.java   | 36 ++++++++----
 .../org/apache/commons/lang3/CharRangeTest.java    | 64 ++++++++++++++++++++++
 2 files changed, 88 insertions(+), 12 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/CharRange.java 
b/src/main/java/org/apache/commons/lang3/CharRange.java
index 1ea3c4a11..8b18790f2 100644
--- a/src/main/java/org/apache/commons/lang3/CharRange.java
+++ b/src/main/java/org/apache/commons/lang3/CharRange.java
@@ -58,14 +58,12 @@ private static final class CharacterIterator implements 
Iterator<Character> {
         private CharacterIterator(final CharRange r) {
             range = r;
             hasNext = true;
-            if (range.negated) {
+            if (range.isEmpty()) {
+                // This range is an empty set
+                hasNext = false;
+            } else if (range.negated) {
                 if (range.start == Character.MIN_VALUE) {
-                    if (range.end == Character.MAX_VALUE) {
-                        // This range is an empty set
-                        hasNext = false;
-                    } else {
-                        current = (char) (range.end + 1);
-                    }
+                    current = (char) (range.end + 1);
                 } else {
                     current = Character.MIN_VALUE;
                 }
@@ -269,15 +267,15 @@ public boolean contains(final CharRange range) {
         }
         if (range.negated) {
             // range denotes [0, range.start - 1] union [range.end + 1, 
Character.MAX_VALUE]
-            final boolean lowEmpty = range.start == Character.MIN_VALUE;
-            final boolean highEmpty = range.end == Character.MAX_VALUE;
-            if (lowEmpty && highEmpty) {
+            if (range.isEmpty()) {
                 return true; // range denotes the empty set
             }
-            if (lowEmpty) {
+            if (range.start == Character.MIN_VALUE) {
+                // range denotes [range.end + 1, Character.MAX_VALUE]
                 return end == Character.MAX_VALUE && start <= range.end + 1;
             }
-            if (highEmpty) {
+            if (range.end == Character.MAX_VALUE) {
+                // range denotes [0, range.start - 1]
                 return start == Character.MIN_VALUE && end + 1 >= range.start;
             }
             return start == Character.MIN_VALUE && end == Character.MAX_VALUE;
@@ -332,6 +330,20 @@ public int hashCode() {
         return Objects.hash(end, negated, start);
     }
 
+    /**
+     * Checks if this range denotes the empty set.
+     *
+     * <p>A plain (non-negated) range always contains at least one character 
and is
+     * therefore never empty. A negated range is empty if and only if it 
excludes the
+     * entire character space, i.e. if it was created via
+     * {@code isNotIn(Character.MIN_VALUE, Character.MAX_VALUE)}.</p>
+     *
+     * @return {@code true} if this range contains no characters, {@code 
false} otherwise.
+     */
+    boolean isEmpty() {
+        return negated && start == Character.MIN_VALUE && end == 
Character.MAX_VALUE;
+    }
+
     /**
      * Is this {@link CharRange} negated.
      *
diff --git a/src/test/java/org/apache/commons/lang3/CharRangeTest.java 
b/src/test/java/org/apache/commons/lang3/CharRangeTest.java
index c401c7693..d7dde92e0 100644
--- a/src/test/java/org/apache/commons/lang3/CharRangeTest.java
+++ b/src/test/java/org/apache/commons/lang3/CharRangeTest.java
@@ -294,6 +294,51 @@ void testContainsNullArg() {
         assertEquals("range", e.getMessage());
     }
 
+    @Test
+    void testEmptySet() {
+        // isNotIn(MIN_VALUE, MAX_VALUE) denotes the empty set: it excludes 
the entire character space.
+        final CharRange emptySet = CharRange.isNotIn(Character.MIN_VALUE, 
Character.MAX_VALUE);
+        assertTrue(emptySet.isNegated());
+        assertEquals(Character.MIN_VALUE, emptySet.getStart());
+        assertEquals(Character.MAX_VALUE, emptySet.getEnd());
+        // The constructor reverses reversed endpoints, so the reversed 
construction is the same empty set.
+        assertEquals(emptySet, CharRange.isNotIn(Character.MAX_VALUE, 
Character.MIN_VALUE));
+        assertEquals(emptySet.hashCode(), 
CharRange.isNotIn(Character.MAX_VALUE, Character.MIN_VALUE).hashCode());
+
+        // It contains no characters, including both extremes of the character 
space.
+        assertFalse(emptySet.contains(Character.MIN_VALUE));
+        assertFalse(emptySet.contains('a'));
+        assertFalse(emptySet.contains('x'));
+        assertFalse(emptySet.contains('z'));
+        assertFalse(emptySet.contains(Character.MAX_VALUE));
+
+        // It is contained in every range, plain or negated, single character 
or not.
+        assertTrue(CharRange.is('a').contains(emptySet));
+        assertTrue(CharRange.is('x').contains(emptySet));
+        assertTrue(CharRange.isIn('a', 'z').contains(emptySet));
+        assertTrue(CharRange.isIn(Character.MIN_VALUE, 
Character.MAX_VALUE).contains(emptySet));
+        assertTrue(CharRange.isNot('a').contains(emptySet));
+        assertTrue(CharRange.isNotIn('a', 'z').contains(emptySet));
+        assertTrue(CharRange.isNotIn(Character.MIN_VALUE, 
'z').contains(emptySet));
+        assertTrue(CharRange.isNotIn('a', 
Character.MAX_VALUE).contains(emptySet));
+
+        // Iterating over it yields no characters.
+        final Iterator<Character> emptySetIt = emptySet.iterator();
+        assertNotNull(emptySetIt);
+        assertFalse(emptySetIt.hasNext());
+        assertThrows(NoSuchElementException.class, emptySetIt::next);
+
+        // Ranges that exclude a boundary but not the entire character space 
are NOT empty ...
+        final CharRange fromB = CharRange.isNotIn(Character.MIN_VALUE, 'a'); 
// denotes ['b', MAX_VALUE]
+        final CharRange upToA = CharRange.isNotIn('a', Character.MAX_VALUE); 
// denotes [MIN_VALUE, 'a']
+        assertTrue(fromB.contains(Character.MAX_VALUE));
+        assertTrue(upToA.contains(Character.MIN_VALUE));
+        // ... so, unlike the empty set, they are not contained in every range.
+        assertFalse(CharRange.is('a').contains(fromB));
+        assertFalse(CharRange.is('b').contains(upToA));
+        assertFalse(upToA.contains(fromB));
+    }
+
     @Test
     void testEquals_Object() {
         final CharRange rangea = CharRange.is('a');
@@ -381,6 +426,25 @@ void testHashCodeLang1802() {
         assertEquals(range1.hashCode(), sameAsRange1.hashCode(), "Equal ranges 
should have equal hash codes");
     }
 
+    @Test
+    void testIsEmpty() {
+        // Only a negated range over the entire character space is empty.
+        assertTrue(CharRange.isNotIn(Character.MIN_VALUE, 
Character.MAX_VALUE).isEmpty());
+        // The constructor reverses reversed endpoints, so this is the same 
empty set.
+        assertTrue(CharRange.isNotIn(Character.MAX_VALUE, 
Character.MIN_VALUE).isEmpty());
+
+        // Negated ranges that exclude a boundary or an interior but not the 
entire character space are not empty.
+        assertFalse(CharRange.isNotIn(Character.MIN_VALUE, 'a').isEmpty());
+        assertFalse(CharRange.isNotIn('a', Character.MAX_VALUE).isEmpty());
+        assertFalse(CharRange.isNot('a').isEmpty());
+        assertFalse(CharRange.isNotIn('a', 'z').isEmpty());
+
+        // Plain ranges always contain at least one character, even the full 
character space.
+        assertFalse(CharRange.is('a').isEmpty());
+        assertFalse(CharRange.isIn('a', 'z').isEmpty());
+        assertFalse(CharRange.isIn(Character.MIN_VALUE, 
Character.MAX_VALUE).isEmpty());
+    }
+
     @Test
     void testIterator() {
         final CharRange a = CharRange.is('a');

Reply via email to