diff --git a/src/main/java/org/apache/commons/lang3/CharRange.java b/src/main/java/org/apache/commons/lang3/CharRange.java index f63c8a56d..8cff4bf6f 100644 --- a/src/main/java/org/apache/commons/lang3/CharRange.java +++ b/src/main/java/org/apache/commons/lang3/CharRange.java @@ -17,6 +17,8 @@ package org.apache.commons.lang3; import java.io.Serializable; +import java.util.Iterator; +import java.util.NoSuchElementException; /** *

A contiguous range of characters, optionally negated.

@@ -241,5 +243,96 @@ public final class CharRange implements Serializable { } return iToString; } - + + // Expansions + //----------------------------------------------------------------------- + /** + *

Returns an iterator which can be used to walk through the characters described by this range.

+ * + * @return an iterator to the chars represented by this range + */ + public Iterator iterator() { + return new CharacterIterator(this); + } + + static class CharacterIterator implements Iterator { + /** The currect character */ + private char current; + + private CharRange range; + private boolean hasNext; + + public CharacterIterator(CharRange r) { + range = r; + hasNext = true; + + if (range.negated) { + if (range.start == 0) { + if (range.end == Character.MAX_VALUE) { + // This range is an empty set + hasNext = false; + } else { + current = (char) (range.end + 1); + } + } else { + current = 0; + } + } else { + current = range.start; + } + } + + private void prepareNext() { + if (range.negated) { + if (current == Character.MAX_VALUE) { + hasNext = false; + } else if (current + 1 == range.start) { + if (range.end == Character.MAX_VALUE) { + hasNext = false; + } else { + current = (char) (range.end + 1); + } + } else { + current = (char) (current + 1); + } + } else if (current < range.end) { + current = (char) (current + 1); + } else { + hasNext = false; + } + } + + /** + * Has the iterator not reached the end character yet? + * + * @return true if the iterator has yet to reach the character date + */ + public boolean hasNext() { + return hasNext; + } + + /** + * Return the next character in the iteration + * + * @return Character for the next character + */ + public Object next() { + if (hasNext == false) { + throw new NoSuchElementException(); + } + char cur = current; + prepareNext(); + return Character.valueOf(cur); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + public void remove() { + throw new UnsupportedOperationException(); + } + } } diff --git a/src/test/java/org/apache/commons/lang3/CharRangeTest.java b/src/test/java/org/apache/commons/lang3/CharRangeTest.java index 1bc1cc3f2..d84f17167 100644 --- a/src/test/java/org/apache/commons/lang3/CharRangeTest.java +++ b/src/test/java/org/apache/commons/lang3/CharRangeTest.java @@ -19,6 +19,8 @@ package org.apache.commons.lang3; import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.NoSuchElementException; import junit.framework.TestCase; @@ -301,7 +303,73 @@ public class CharRangeTest extends TestCase { assertEquals("The Range must not be null", e.getMessage()); } } - + + public void testIterator() { + CharRange a = CharRange.is('a'); + CharRange ad = CharRange.isIn('a', 'd'); + CharRange nota = CharRange.isNot('a'); + CharRange emptySet = CharRange.isNotIn((char) 0, Character.MAX_VALUE); + CharRange notFirst = CharRange.isNotIn((char) 1, Character.MAX_VALUE); + CharRange notLast = CharRange.isNotIn((char) 0, (char) (Character.MAX_VALUE - 1)); + + Iterator aIt = a.iterator(); + assertNotNull(aIt); + assertTrue(aIt.hasNext()); + assertEquals(Character.valueOf('a'), aIt.next()); + assertFalse(aIt.hasNext()); + + Iterator adIt = ad.iterator(); + assertNotNull(adIt); + assertTrue(adIt.hasNext()); + assertEquals(Character.valueOf('a'), adIt.next()); + assertEquals(Character.valueOf('b'), adIt.next()); + assertEquals(Character.valueOf('c'), adIt.next()); + assertEquals(Character.valueOf('d'), adIt.next()); + assertFalse(adIt.hasNext()); + + Iterator notaIt = nota.iterator(); + assertNotNull(notaIt); + assertTrue(notaIt.hasNext()); + while (notaIt.hasNext()) { + Character c = (Character) notaIt.next(); + assertFalse('a' == c.charValue()); + } + + Iterator emptySetIt = emptySet.iterator(); + assertNotNull(emptySetIt); + assertFalse(emptySetIt.hasNext()); + try { + emptySetIt.next(); + fail("Should throw NoSuchElementException"); + } catch (NoSuchElementException e) { + assertTrue(true); + } + + Iterator notFirstIt = notFirst.iterator(); + assertNotNull(notFirstIt); + assertTrue(notFirstIt.hasNext()); + assertEquals(Character.valueOf((char) 0), notFirstIt.next()); + assertFalse(notFirstIt.hasNext()); + try { + notFirstIt.next(); + fail("Should throw NoSuchElementException"); + } catch (NoSuchElementException e) { + assertTrue(true); + } + + Iterator notLastIt = notLast.iterator(); + assertNotNull(notLastIt); + assertTrue(notLastIt.hasNext()); + assertEquals(Character.valueOf(Character.MAX_VALUE), notLastIt.next()); + assertFalse(notLastIt.hasNext()); + try { + notLastIt.next(); + fail("Should throw NoSuchElementException"); + } catch (NoSuchElementException e) { + assertTrue(true); + } + } + //----------------------------------------------------------------------- public void testSerialization() { CharRange range = CharRange.is('a');