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