Index: /Users/edwin/workspace/groovy/src/main/groovy/lang/IntRange.java =================================================================== --- /Users/edwin/workspace/groovy/src/main/groovy/lang/IntRange.java (revision 4080) +++ /Users/edwin/workspace/groovy/src/main/groovy/lang/IntRange.java (working copy) @@ -46,25 +46,113 @@ package groovy.lang; import java.util.AbstractList; +import java.util.Collection; import java.util.Iterator; import java.util.List; -import org.codehaus.groovy.runtime.InvokerHelper; +import javax.naming.OperationNotSupportedException; + +import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.runtime.IteratorClosureAdapter; /** - * Represents a list of Integer objects from a specified int up to and including - * a given and to. + * Represents a list of Integer objects from a specified int up (or down) to and including + * a given to.
*
+ * This class is a copy of {@link ObjectRange} optimized for int. If you make any
+ * changes to this class, you might consider making parallel changes to {@link ObjectRange}.
+ *
* @author James Strachan
* @version $Revision$
*/
public class IntRange extends AbstractList implements Range {
+ /**
+ * Iterates through each number in an IntRange.
+ */
+ private class IntRangeIterator implements Iterator {
+ /**
+ * Counts from 0 up to size - 1.
+ */
+ int index = 0;
+
+ /**
+ * The number of values in the range.
+ */
+ int size = size();
+
+ /**
+ * The next value to return.
+ */
+ int value = (reverse) ? to : from;
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasNext() {
+ return index < size;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object next() {
+ if (index++ > 0) {
+ if (index > size) {
+ return null;
+ }
+ else {
+ if (reverse) {
+ --value;
+ }
+ else {
+ ++value;
+ }
+ }
+ }
+ return new Integer(value);
+ }
+
+ /**
+ * Not supported.
+ *
+ * @throws OperationNotSupportedException always
+ */
+ public void remove() {
+ IntRange.this.remove(index);
+ }
+ }
+
+ /**
+ * The first number in the range. from is always less than or equal to to.
+ */
private int from;
+
+ /**
+ * The last number in the range. to is always greater than or eqaul to from.
+ */
private int to;
+
+ /**
+ * If false, counts up from from to to. Otherwise, counts down
+ * from to to from.
+ */
private boolean reverse;
+ /**
+ * Creates a new IntRange. If from is greater
+ * than to, a reverse range is created with
+ * from and to swapped.
+ *
+ * @param from
+ * the first number in the range.
+ * @param to
+ * the last number in the range.
+ *
+ * @throws IllegalArgumentException
+ * if the range would contain more than
+ * {@link Integer#MAX_VALUE} values.
+ */
public IntRange(int from, int to) {
if (from > to) {
this.from = to;
@@ -75,61 +163,109 @@
this.from = from;
this.to = to;
}
+
+ // size() an integer so ranges can have no more than Integer.MAX_VALUE elements
+ if (this.to - this.from >= Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("range must have no more than " + Integer.MAX_VALUE + " elements");
+ }
}
- protected IntRange(int from, int to, boolean reverse) {
+ /**
+ * Creates a new IntRange.
+ *
+ * @param from
+ * the first value in the range.
+ * @param to
+ * the last value in the range.
+ * @param reverse
+ * true if the range should count from
+ * to to from.
+ *
+ * @throws IllegalArgumentException
+ * if from is greater than to.
+ */
+ protected IntRange(int from, int to, boolean reverse) {
+ if (from > to) {
+ throw new IllegalArgumentException("'from' must be less than or equal to 'to'");
+ }
+
this.from = from;
this.to = to;
this.reverse = reverse;
}
+ /**
+ * Determines if this object is equal to another object. Delegates to
+ * {@link AbstractList#equals(Object)} if that is anthing
+ * other than an {@link IntRange}.
+ *
+ *
+ * It is not necessary to override hashCode, as
+ * {@link AbstractList#hashCode()} provides a suitable hash code.
+ *
+ * Note that equals is generally handled by {@link DefaultGroovyMethods#equals(List, List)} instead of this
+ * method.
+ *
+ * @param that
+ * the object to compare
+ *
+ * @return true if the objects are equal
+ */
public boolean equals(Object that) {
- if (that instanceof IntRange) {
- return equals((IntRange) that);
- }
- else if (that instanceof List) {
- return equals((List) that);
- }
- return false;
+ return that instanceof IntRange ? equals((IntRange) that) : super.equals(that);
}
- public boolean equals(List that) {
- int size = size();
- if (that.size() == size) {
- for (int i = 0; i < size; i++) {
- if (!InvokerHelper.compareEqual(get(i), that.get(i))) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
+ /**
+ * Compares an {@link IntRange} to another {@link IntRange}.
+ *
+ * @return true if the ranges are equal
+ */
public boolean equals(IntRange that) {
- return this.reverse == that.reverse && this.from == that.from && this.to == that.to;
+ return that != null && this.reverse == that.reverse && this.from == that.from && this.to == that.to;
}
+ /**
+ * {@inheritDoc}
+ */
public Comparable getFrom() {
return new Integer(from);
}
+ /**
+ * {@inheritDoc}
+ */
public Comparable getTo() {
return new Integer(to);
}
+ /**
+ * Gets the 'from' value as an integer.
+ *
+ * @return the 'from' value as an integer.
+ */
public int getFromInt() {
return from;
}
+ /**
+ * Gets the 'to' value as an integer.
+ *
+ * @return the 'to' value as an integer.
+ */
public int getToInt() {
return to;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean isReverse() {
return reverse;
}
+ /**
+ * {@inheritDoc}
+ */
public Object get(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
@@ -141,47 +277,23 @@
return new Integer(value);
}
+ /**
+ * {@inheritDoc}
+ */
public int size() {
return to - from + 1;
}
- public int hashCode() {
- return from ^ to + (reverse ? 1 : 0);
- }
-
+ /**
+ * {@inheritDoc}
+ */
public Iterator iterator() {
- return new Iterator() {
- int index = 0;
- int size = size();
- int value = (reverse) ? to : from;
-
- public boolean hasNext() {
- return index < size;
- }
-
- public Object next() {
- if (index++ > 0) {
- if (index > size) {
- return null;
- }
- else {
- if (reverse) {
- --value;
- }
- else {
- ++value;
- }
- }
- }
- return new Integer(value);
- }
-
- public void remove() {
- IntRange.this.remove(index);
- }
- };
+ return new IntRangeIterator();
}
+ /**
+ * {@inheritDoc}
+ */
public List subList(int fromIndex, int toIndex) {
if (fromIndex < 0) {
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
@@ -192,29 +304,54 @@
if (fromIndex > toIndex) {
throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
}
+
+ if (fromIndex == toIndex) {
+ return new EmptyRange(new Integer(from));
+ }
+
return new IntRange(fromIndex + this.from, toIndex + this.from - 1, reverse);
}
+ /**
+ * {@inheritDoc}
+ */
public String toString() {
return (reverse) ? "" + to + ".." + from : "" + from + ".." + to;
}
+ /**
+ * {@inheritDoc}
+ */
public String inspect() {
return toString();
}
+ /**
+ * {@inheritDoc}
+ */
public boolean contains(Object value) {
if (value instanceof Integer) {
Integer integer = (Integer) value;
int i = integer.intValue();
return i >= from && i <= to;
- } else if (value instanceof IntRange) {
- IntRange range = (IntRange) value;
- return from<=range.from && range.to<=to;
}
return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean containsAll(Collection other) {
+ if (other instanceof IntRange) {
+ final IntRange range = (IntRange) other;
+ return this.from <= range.from && range.to <= this.to;
+ }
+ return super.containsAll(other);
}
+ /**
+ * {@inheritDoc}
+ */
public void step(int step, Closure closure) {
if (reverse) {
step = -step;
@@ -235,6 +372,9 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
public List step(int step) {
IteratorClosureAdapter adapter = new IteratorClosureAdapter(this);
step(step, adapter);
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/IntRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/IntRangeTest.java (revision 4080)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/IntRangeTest.java (working copy)
@@ -45,106 +45,59 @@
*/
package groovy.lang;
-import java.util.List;
-import junit.framework.TestCase;
-
/**
+ * Provides unit tests for the IntRange class.
+ *
* @author James Strachan
* @version $Revision$
*/
-public class IntRangeTest extends TestCase {
-
- public void testSize() {
- IntRange r = createRange(0, 10);
- assertEquals("Size of " + r, 11, r.size());
- r = createRange(0, 1);
- assertEquals("Size of " + r, 2, r.size());
- r = createRange(0, 0);
- assertEquals("Size of " + r, 1, r.size());
- }
-
- public void testProperties() {
- IntRange r = createRange(0, 10);
- assertEquals("from", 0, r.getFromInt());
- assertEquals("to", 10, r.getToInt());
- }
-
- public void testGet() {
- IntRange r = createRange(10, 20);
- for (int i = 0; i <= 10; i++) {
- Integer value = (Integer) r.get(i);
- assertEquals("Item at index: " + i, i + 10, value.intValue());
- }
- }
-
- public void testGetOutOfRange() {
- IntRange r = createRange(10, 20);
-
+public class IntRangeTest extends NumberRangeTest {
+
+ public void testCreateTooBigRange() {
try {
- r.get(-1);
- fail("Should have thrown IndexOut");
+ createRange(0, Integer.MAX_VALUE);
+ fail("too large range accepted");
}
- catch (IndexOutOfBoundsException e) {
- // worked
+ catch (IllegalArgumentException e) {
+ assertTrue("expected exception thrown", true);
}
+ }
+
+ /**
+ * Tests providing invalid arguments to the protected constructor.
+ */
+ public void testInvalidArgumentsToConstructor() {
try {
- r.get(11);
- fail("Should have thrown IndexOut");
+ new IntRange(2, 1, true);
+ fail("invalid range created");
}
- catch (IndexOutOfBoundsException e) {
- // worked
+ catch (IllegalArgumentException e) {
+ assertTrue("expected exception thrown", true);
}
+ }
+ /**
+ * Tests getting the to and from values as ints.
+ */
+ public void testGetToFromInt() {
+ final int from = 3, to = 7;
+ final IntRange range = new IntRange(from, to);
+ assertEquals("wrong 'from'", from, range.getFromInt());
+ assertEquals("wrong 'to'", to, range.getToInt());
}
-
- public void testContains() {
- IntRange r = createRange(10, 20);
-
- assertTrue("contains 11", r.contains(new Integer(11)));
- assertTrue("contains 10", r.contains(new Integer(10)));
- assertTrue("contains 19", r.contains(new Integer(19)));
- assertTrue("contains 20", r.contains(new Integer(20)));
- assertFalse("contains 9", r.contains(new Integer(9)));
- assertFalse("contains 21", r.contains(new Integer(21)));
- assertFalse("contains 100", r.contains(new Integer(100)));
- assertFalse("contains -1", r.contains(new Integer(-1)));
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Comparable createValue(int value) {
+ return new Integer(value);
}
- public void testSubList() {
- IntRange r = createRange(10, 20);
-
- List s = r.subList(2, 4);
-
- IntRange sr = (IntRange) s;
-
- assertEquals("from", 12, sr.getFromInt());
- assertEquals("to", 13, sr.getToInt());
- assertEquals("size", 2, sr.size());
- }
-
- public void testHashCodeAndEquals() {
- IntRange a = createRange(1, 11);
- IntRange b = createRange(1, 11);
- IntRange c = createRange(2, 11);
-
- assertEquals("hashcode", a.hashCode(), b.hashCode());
- assertTrue("hashcode", a.hashCode() != c.hashCode());
-
- assertEquals("a and b", a, b);
- assertFalse("a != c", a.equals(c));
- }
-
- public void testIterator() {
- }
-
- protected IntRange createRange(int from, int to) {
+ /**
+ * {@inheritDoc}
+ */
+ protected Range createRange(int from, int to) {
return new IntRange(from, to);
}
-
- protected void assertEquals(String msg, int expected, Object value) {
- assertEquals(msg, new Integer(expected), value);
- }
-
-
}
Index: /Users/edwin/workspace/groovy/src/main/groovy/lang/ObjectRange.java
===================================================================
--- /Users/edwin/workspace/groovy/src/main/groovy/lang/ObjectRange.java (revision 4080)
+++ /Users/edwin/workspace/groovy/src/main/groovy/lang/ObjectRange.java (working copy)
@@ -56,23 +56,58 @@
/**
* Represents an inclusive list of objects from a value to a value using
- * comparators
- *
+ * comparators.
+ *
+ * This class is similar to {@link IntRange}. If you make any changes to this
+ * class, you might consider making parallel changes to {@link IntRange}.
+ *
* @author James Strachan
* @version $Revision$
*/
public class ObjectRange extends AbstractList implements Range {
+ /**
+ * The first value in the range.
+ */
private Comparable from;
+
+ /**
+ * The last value in the range.
+ */
private Comparable to;
+
+ /**
+ * The cached size, or -1 if not yet computed
+ */
private int size = -1;
+
+ /**
+ * true if the range counts backwards from to to from.
+ */
private final boolean reverse;
+ /**
+ * Creates a new {@link ObjectRange}. Creates a reversed range if
+ * from < to.
+ *
+ * @param from
+ * the first value in the range.
+ * @param to
+ * the last value in the range.
+ */
public ObjectRange(Comparable from, Comparable to) {
+ if (from == null) {
+ throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range");
+ }
+ if (to == null) {
+ throw new IllegalArgumentException("Must specify a non-null value for the 'to' index in a Range");
+ }
+
this.reverse = InvokerHelper.compareGreaterThan(from, to);
if (this.reverse) {
constructorHelper(to, from);
- } else {
+ }
+ else {
constructorHelper(from, to);
}
}
@@ -84,16 +119,19 @@
}
private void constructorHelper(Comparable from, Comparable to) {
- if (from == null) {
- throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range");
+ if (from instanceof Short && to instanceof Short) {
+ this.from = new Integer(((Short) from).intValue());
+ this.to = new Integer(((Short) to).intValue());
}
- if (to == null) {
- throw new IllegalArgumentException("Must specify a non-null value for the 'to' index in a Range");
+ else if (from instanceof Float && to instanceof Float) {
+ this.from = new Double(((Float) from).doubleValue());
+ this.to = new Double(((Float) to).doubleValue());
}
- if (from.getClass() == to.getClass()) {
+ else if (from.getClass() == to.getClass()) {
this.from = from;
this.to = to;
- } else {
+ }
+ else {
this.from = normaliseType(from);
this.to = normaliseType(to);
}
@@ -117,51 +155,49 @@
}
}
- public int hashCode() {
- /** @todo should code this the Josh Bloch way */
- return from.hashCode() ^ to.hashCode() + (reverse ? 1 : 0);
- }
-
+ /**
+ * {@inheritDoc}
+ */
public boolean equals(Object that) {
- if (that instanceof ObjectRange) {
- return equals((ObjectRange) that);
- } else if (that instanceof List) {
- return equals((List) that);
- }
- return false;
+ return (that instanceof ObjectRange) ? equals((ObjectRange) that) : super.equals(that);
}
+ /**
+ * Compares an {@link ObjectRange} to another {@link ObjectRange}.
+ *
+ * @return true if the ranges are equal
+ */
public boolean equals(ObjectRange that) {
- return this.reverse == that.reverse
- && InvokerHelper.compareEqual(this.from, that.from)
- && InvokerHelper.compareEqual(this.to, that.to);
+ return that != null
+ && this.reverse == that.reverse
+ && this.from.equals(that.from)
+ && this.to.equals(that.to);
}
- public boolean equals(List that) {
- int size = size();
- if (that.size() == size) {
- for (int i = 0; i < size; i++) {
- if (!InvokerHelper.compareEqual(get(i), that.get(i))) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
+ /**
+ * {@inheritDoc}
+ */
public Comparable getFrom() {
return from;
}
+ /**
+ * {@inheritDoc}
+ */
public Comparable getTo() {
return to;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean isReverse() {
return reverse;
}
+ /**
+ * {@inheritDoc}
+ */
public Object get(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
@@ -185,6 +221,9 @@
return value;
}
+ /**
+ * {@inheritDoc}
+ */
public Iterator iterator() {
return new Iterator() {
int index = 0;
@@ -215,17 +254,28 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public int size() {
if (size == -1) {
- if (from instanceof Integer && to instanceof Integer) {
- // lets fast calculate the size
+ if (from instanceof Integer && to instanceof Integer
+ || from instanceof Long && to instanceof Long) {
+ // let's fast calculate the size
size = 0;
- int fromNum = ((Integer) from).intValue();
- int toNum = ((Integer) to).intValue();
+ int fromNum = ((Number) from).intValue();
+ int toNum = ((Number) to).intValue();
size = toNum - fromNum + 1;
}
+ else if (from instanceof Character && to instanceof Character) {
+ // let's fast calculate the size
+ size = 0;
+ char fromNum = ((Character) from).charValue();
+ char toNum = ((Character) to).charValue();
+ size = toNum - fromNum + 1;
+ }
else if (from instanceof BigDecimal || to instanceof BigDecimal) {
- // lets fast calculate the size
+ // let's fast calculate the size
size = 0;
BigDecimal fromNum = new BigDecimal("" + from);
BigDecimal toNum = new BigDecimal("" + to);
@@ -233,7 +283,7 @@
size = sizeNum.intValue();
}
else {
- // lets lazily calculate the size
+ // let's lazily calculate the size
size = 0;
Object value = from;
while (to.compareTo(value) >= 0) {
@@ -245,51 +295,45 @@
return size;
}
+ /**
+ * {@inheritDoc}
+ */
public List subList(int fromIndex, int toIndex) {
if (fromIndex < 0) {
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
}
- int size = size();
- if (toIndex > size) {
+ if (toIndex > size()) {
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
}
if (fromIndex > toIndex) {
throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
}
- if (--toIndex >= size) {
- return new ObjectRange((Comparable) get(fromIndex), getTo(), reverse);
- } else {
- return new ObjectRange((Comparable) get(fromIndex), (Comparable) get(toIndex), reverse);
+ if (fromIndex == toIndex) {
+ return new EmptyRange(from);
}
+
+ return new ObjectRange((Comparable) get(fromIndex), (Comparable) get(--toIndex), reverse);
}
+ /**
+ * {@inheritDoc}
+ */
public String toString() {
return (reverse) ? "" + to + ".." + from : "" + from + ".." + to;
}
+ /**
+ * {@inheritDoc}
+ */
public String inspect() {
String toText = InvokerHelper.inspect(to);
String fromText = InvokerHelper.inspect(from);
return (reverse) ? "" + toText + ".." + fromText : "" + fromText + ".." + toText;
}
-
- public boolean contains(Comparable value) {
- if (from instanceof BigDecimal || to instanceof BigDecimal) {
- int result = (new BigDecimal("" + from)).compareTo(new BigDecimal("" + value));
- if (result == 0) {
- return true;
- }
- return result < 0 && (new BigDecimal("" + to)).compareTo(new BigDecimal("" + value)) >= 0;
- }
- else {
- int result = from.compareTo(value);
- if (result == 0) {
- return true;
- }
- return result < 0 && to.compareTo(value) >= 0;
- }
- }
-
+
+ /**
+ * {@inheritDoc}
+ */
public void step(int step, Closure closure) {
if (reverse) {
step = -step;
@@ -314,16 +358,35 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
public List step(int step) {
IteratorClosureAdapter adapter = new IteratorClosureAdapter(this);
step(step, adapter);
return adapter.asList();
}
+ /**
+ * Increments by one
+ *
+ * @param value
+ * the value to increment
+ *
+ * @return the incremented value
+ */
protected Object increment(Object value) {
return InvokerHelper.invokeMethod(value, "next", null);
}
+ /**
+ * Decrements by one
+ *
+ * @param value
+ * the value to decrement
+ *
+ * @return the decremented value
+ */
protected Object decrement(Object value) {
return InvokerHelper.invokeMethod(value, "previous", null);
}
@@ -331,7 +394,8 @@
private static Comparable normaliseType(final Comparable operand) {
if (operand instanceof Character) {
return new Integer(((Character) operand).charValue());
- } else if (operand instanceof String) {
+ }
+ else if (operand instanceof String) {
final String string = (String) operand;
if (string.length() == 1)
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/NumberRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/NumberRangeTest.java (revision 0)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/NumberRangeTest.java (revision 0)
@@ -0,0 +1,770 @@
+/*
+ $Id: NumberRangeTest.java,v 1.2 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Codehaus. For written permission,
+ please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+ nor may "groovy" appear in their names without prior written
+ permission of The Codehaus. "groovy" is a registered
+ trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+ http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Provides unit tests for ranges of numbers.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public abstract class NumberRangeTest extends TestCase {
+
+ /**
+ * Records the values passed to a closure.
+ */
+ protected static class RecordingClosure extends Closure {
+ /**
+ * Holds the values passed in
+ */
+ final List callLog;
+
+ /**
+ * Creates a new RecordingClosure
+ *
+ * @param callLog is filled with the values passed to doCall
+ */
+ RecordingClosure(final List callLog) {
+ super(null);
+ this.callLog = callLog;
+ }
+
+ /**
+ * Stores params in the callLog.
+ *
+ * @param params the parameters.
+ *
+ * @return null
+ */
+ public Object doCall(final Object params) {
+ callLog.add(params);
+ return null;
+ }
+ }
+
+ /**
+ * Creates a {@link Range} to test.
+ *
+ * @param from
+ * the first value in the range.
+ * @param to
+ * the last value in the range.
+ *
+ * @return a {@link Range} to test
+ */
+ protected abstract Range createRange(final int from, final int to);
+
+ /**
+ * Creates a value in the range.
+ *
+ * @param value
+ * the value to create.
+ *
+ * @return a value in the range.
+ */
+ protected abstract Comparable createValue(final int value);
+
+ /**
+ * Tests hashCode and equals comparing one {@link IntRange} to another {@link IntRange}.
+ *
+ */
+ public final void testHashCodeAndEquals() {
+ Range a = createRange(1, 11);
+ Range b = createRange(1, 11);
+ Range c = createRange(2, 11);
+
+ assertEquals("hashcode", a.hashCode(), b.hashCode());
+ assertTrue("hashcode", a.hashCode() != c.hashCode());
+
+ assertEquals("a and b", a, b);
+ assertFalse("a != c", a.equals(c));
+ }
+
+ /**
+ * Tests using different classes for 'from' and 'to'.
+ */
+ public void testDifferentClassesForFromAndTo() {
+ final Integer from = new Integer(1);
+ final Comparable to = createValue(5);
+ final Range range = new ObjectRange(from, to);
+
+ assertEquals("wrong 'from' value", from, range.getFrom());
+ assertEquals("wrong 'to' value", to, range.getTo());
+ }
+
+ /**
+ * Tests a null 'from' value.
+ *
+ */
+ public void testNullFrom() {
+ try {
+ new ObjectRange(null, createValue(5));
+ fail("null 'from' accepted");
+ }
+ catch (IllegalArgumentException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ }
+
+ /**
+ * Tests a null 'to' value.
+ *
+ */
+ public void testNullTo() {
+ try {
+ new ObjectRange(createValue(23), null);
+ fail("null 'to' accepted");
+ }
+ catch (IllegalArgumentException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ }
+ /**
+ * Tests stepping through a range by two with a closure.
+ */
+ public void testStepByTwoWithClosure() {
+ final List callLog = new ArrayList();
+ final Closure closure = new RecordingClosure(callLog);
+
+ final Range range = createRange(0, 4);
+ range.step(2, closure);
+
+ assertEquals("wrong number of calls to closure", 3, callLog.size());
+ final Iterator iter = callLog.iterator();
+ for (int i = 0; i <= 4; i += 2) {
+ assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+ }
+ }
+
+ /**
+ * Tests iterating over a one-element range.
+ */
+ public void testOneElementRange() {
+ final Range range = createRange(1, 1);
+ int next = 1;
+ for (Iterator iter = range.iterator(); iter.hasNext();) {
+ final Number number = (Number) iter.next();
+ assertEquals("wrong number", createValue(next++), number);
+ }
+ assertEquals("wrong number of elements in iteration", 2, next);
+ }
+
+ /**
+ * Tests stepping through a reversed range by two with a closure.
+ */
+ public void testReverseStepByTwoWithClosure() {
+ final List callLog = new ArrayList();
+ final Closure closure = new RecordingClosure(callLog);
+
+ final Range range = createRange(4, 0);
+ range.step(2, closure);
+
+ assertEquals("wrong number of calls to closure", 3, callLog.size());
+ final Iterator iter = callLog.iterator();
+ for (int i = 4; i >= 0; i -= 2) {
+ assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+ }
+ }
+
+ /**
+ * Tests stepping through a range with a closure.
+ */
+ public void testStepByOneWithClosure() {
+ final List callLog = new ArrayList();
+ final Closure closure = new RecordingClosure(callLog);
+
+ final Range range = createRange(1, 5);
+ range.step(1, closure);
+
+ assertEquals("wrong number of calls to closure", 5, callLog.size());
+ final Iterator iter = callLog.iterator();
+ for (int i = 1; i <= 5; i++) {
+ assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+ }
+ }
+
+ /**
+ * Tests stepping through a reversed range by one with a closure.
+ */
+ public void testReverseStepByOneWithClosure() {
+ final List callLog = new ArrayList();
+ final Closure closure = new RecordingClosure(callLog);
+
+ final Range range = createRange(5, 1);
+ range.step(1, closure);
+
+ assertEquals("wrong number of calls to closure", 5, callLog.size());
+ final Iterator iter = callLog.iterator();
+ for (int i = 5; i >= 1; i--) {
+ assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+ }
+ }
+
+ /**
+ * Tests stepping backwards through a range with a closure.
+ */
+ public void testNegativeStepByOneWithClosure() {
+ final List callLog = new ArrayList();
+ final Closure closure = new RecordingClosure(callLog);
+
+ final Range range = createRange(1, 5);
+ range.step(-1, closure);
+
+ assertEquals("wrong number of calls to closure", 5, callLog.size());
+ final Iterator iter = callLog.iterator();
+ for (int i = 5; i >= 1; i--) {
+ assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+ }
+ }
+
+ /**
+ * Tests stepping backwards through a reversed range with a closure.
+ */
+ public void testNegativeReverseStepByOneWithClosure() {
+ final List callLog = new ArrayList();
+ final Closure closure = new RecordingClosure(callLog);
+
+ final Range range = createRange(5, 1);
+ range.step(-1, closure);
+
+ assertEquals("wrong number of calls to closure", 5, callLog.size());
+ final Iterator iter = callLog.iterator();
+ for (int i = 1; i <= 5; i++) {
+ assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+ }
+ }
+
+ /**
+ * Tests stepping backwards through a range with a step size greater than the range size.
+ */
+ public void testStepLargerThanRange() {
+ final List callLog = new ArrayList();
+ final Closure closure = new RecordingClosure(callLog);
+
+ final Range range = createRange(1, 5);
+
+ range.step(6, closure);
+ assertEquals("wrong number of calls to closure", 1, callLog.size());
+ assertEquals("wrong value", createValue(1), callLog.get(0));
+
+ final List stepList = range.step(6);
+ assertEquals("wrong number of values in result", 1, stepList.size());
+ assertEquals("wrong value", createValue(1), callLog.get(0));
+ }
+
+ /**
+ * Tests stepping through a range by one.
+ */
+ public void testStepByOne() {
+ final Range range = createRange(1, 5);
+ final List result = range.step(1);
+
+ assertEquals("wrong number of calls", 5, result.size());
+ final Iterator iter = result.iterator();
+ for (int i = 1; i <= 5; i++) {
+ assertEquals("incorrect value in result", createValue(i), iter.next());
+ }
+ }
+
+ /**
+ * Tests stepping through a range by two.
+ */
+ public void testStepByTwo() {
+ final Range range = createRange(1, 5);
+ final List result = range.step(2);
+
+ assertEquals("wrong number of calls", 3, result.size());
+ final Iterator iter = result.iterator();
+ for (int i = 1; i <= 5; i += 2) {
+ assertEquals("incorrect value in result", createValue(i), iter.next());
+ }
+ }
+
+ /**
+ * Tests getting the size.
+ */
+ public void testSize() {
+ Range range = createRange(0, 10);
+ assertEquals("Size of " + range, 11, range.size());
+ range = createRange(0, 1);
+ assertEquals("Size of " + range, 2, range.size());
+ range = createRange(0, 0);
+ assertEquals("Size of " + range, 1, range.size());
+ }
+
+ /**
+ * Tests asking for an index outside of the valid range
+ *
+ */
+ public void testGetOutOfRange() {
+ Range r = createRange(10, 20);
+
+ try {
+ r.get(-1);
+ fail("Should have thrown IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ try {
+ r.get(11);
+ fail("Should have thrown IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException e) {
+ assertTrue("expected exception thrown", true);
+ }
+
+ }
+
+ /**
+ * Tests getting a sub list.
+ */
+ public void testSubList() {
+ Range range = createRange(0, 5);
+
+ List subList = range.subList(2, 4);
+ assertEquals("size", 2, subList.size());
+
+ assertTrue("sublist not a range", subList instanceof Range);
+ Range subListRange = (Range) subList;
+
+ assertEquals("from", createValue(2), subListRange.getFrom());
+ assertEquals("to", createValue(3), subListRange.getTo());
+
+ subList = range.subList(0, 6);
+ assertEquals("size", 6, subList.size());
+
+ assertTrue("sublist not a range", subList instanceof Range);
+ subListRange = (Range) subList;
+
+ assertEquals("from", createValue(0), subListRange.getFrom());
+ assertEquals("to", createValue(5), subListRange.getTo());
+ }
+
+ /**
+ * Tests creating a sub list with a negative "from" index.
+ */
+ public void testSubListNegativeFrom() {
+ try {
+ final Range range = createRange(1, 5);
+ range.subList(-1, 3);
+ fail("accepted sub list with negative index");
+ }
+ catch (IndexOutOfBoundsException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ }
+
+ /**
+ * Tests creating a sub list with an out of range "to" index.
+ */
+ public void testSubListOutOfRangeTo() {
+ try {
+ final Range range = createRange(0, 3);
+ range.subList(0, 5);
+ fail("accepted sub list with invalid 'to'");
+ }
+ catch (IndexOutOfBoundsException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ }
+
+ /**
+ * Tests creating a sub list with "from" grater than "to."
+ */
+ public void testSubListFromGreaterThanTo() {
+ try {
+ final Range range = createRange(1, 5);
+ range.subList(3, 2);
+ fail("accepted sub list with 'from' greater than 'to'");
+ }
+ catch (IllegalArgumentException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ }
+
+ /**
+ * Tests creating an empty sub list.
+ */
+ public void testEmptySubList() {
+ final Range range = createRange(1, 5);
+
+ List subList = range.subList(0, 0);
+ assertEquals("wrong number of elements in sub list", 0, subList.size());
+
+ subList = range.subList(2, 2);
+ assertEquals("wrong number of elements in sub list", 0, subList.size());
+ }
+
+ /**
+ * Tests iterating over a non-reversed range.
+ */
+ public void testIterate() {
+ final Range range = createRange(1, 5);
+ int next = 1;
+ final Iterator iter = range.iterator();
+ while (iter.hasNext()) {
+ final Object value = iter.next();
+ assertEquals("wrong next value", createValue(next++), value);
+ }
+ assertEquals("wrong number of elements in iteration", 6, next);
+ assertNull("got element after iterator finished", iter.next());
+ }
+
+ /**
+ * Tests removing an element from the range using an iterator (not supported).
+ */
+ public void testRemoveFromIterator() {
+ final Range range = createRange(1, 5);
+
+ try {
+ final Iterator iter = range.iterator();
+ iter.remove();
+ fail("successfully removed an element using an iterator");
+ }
+ catch (UnsupportedOperationException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ }
+
+ /**
+ * Tests iterating over a reversed range.
+ */
+ public void testIterateReversed() {
+ final Range range = createRange(5, 1);
+ int next = 5;
+ for (Iterator iter = range.iterator(); iter.hasNext();) {
+ assertEquals("wrong number", createValue(next--), iter.next());
+ }
+ assertEquals("wrong number of elements in iteration", 0, next);
+ }
+
+ /**
+ * Tests creating an IntRange with from > to.
+ */
+ public void testFromGreaterThanTo() {
+ final int from = 9;
+ final int to = 0;
+ final Range range = createRange(from, to);
+
+ assertTrue("range not reversed", range.isReverse());
+
+ // make sure to/from are swapped
+ assertEquals("from incorrect", createValue(to), range.getFrom());
+ assertEquals("to incorrect", createValue(from), range.getTo());
+
+ assertEquals("wrong size", 10, range.size());
+
+ assertEquals("wrong first element", createValue(9), range.get(0));
+ assertEquals("wrong last element", createValue(0), range.get(9));
+ }
+
+ /**
+ * Tests creating an IntRange with from == to.
+ */
+ public void testFromEqualsTo() {
+ final Range range = createRange(5, 5);
+
+ assertFalse("range reversed", range.isReverse());
+ assertEquals("wrong size", 1, range.size());
+ }
+
+ /**
+ * Tests creating an IntRange with from < to.
+ */
+ public void testFromLessThanTo() {
+ final int from = 1;
+ final int to = 4;
+ final Range range = createRange(from, to);
+
+ assertFalse("range reversed", range.isReverse());
+
+ assertEquals("to incorrect", createValue(from), range.getFrom());
+ assertEquals("from incorrect", createValue(to), range.getTo());
+
+ assertEquals("wrong size", 4, range.size());
+ }
+
+ /**
+ * Making a range equal a list is not actually possible, since list.equals(range) will not evaluate to
+ * true and equals should be symmetric.
+ */
+ public void testEqualsList() {
+ final List list = new ArrayList();
+ list.add(createValue(1));
+ list.add(createValue(2));
+
+ final Range range = createRange(1, 2);
+
+ // cast to Object to test routing through equals(Object)
+ assertTrue("range does not equal list", range.equals((Object) list));
+ assertTrue("list does not equal range", list.equals(range));
+ assertEquals("hash codes are not equal", range.hashCode(), list.hashCode());
+
+ // compare lists that are the same size but contain different elements
+ list.set(0, createValue(3));
+ assertFalse("range equals list", range.equals(list));
+ assertFalse("list equals range", list.equals(range));
+ assertFalse("hash codes are equal", range.hashCode() == list.hashCode());
+
+ // compare a list longer than the range
+ list.set(0, createValue(1));
+ list.add(createValue(3));
+ assertFalse("range equals list", range.equals(list));
+ assertFalse("list equals range", list.equals(range));
+ assertFalse("hash are equal", range.hashCode() == list.hashCode());
+
+ // compare a list shorter than the range
+ list.remove(2);
+ list.remove(1);
+ assertFalse("range equals list", range.equals(list));
+ assertFalse("list equals range", list.equals(range));
+ assertFalse("hash are equal", range.hashCode() == list.hashCode());
+ }
+
+ /**
+ * Tests comparing {@link Range} to an object that is not a {@link Range}.
+ */
+ public void testEqualsNonRange() {
+ final Range range = createRange(1, 5);
+ assertFalse("range equal to string", range.equals("hello"));
+ }
+
+ /**
+ * Tests comparing a {@link Range} cast to an {@link Object}
+ */
+ public void testEqualsRangeAsObject() {
+ final Range range1 = createRange(1, 5);
+ final Range range2 = createRange(1, 5);
+ assertTrue("ranges not equal", range1.equals((Object) range2));
+ }
+
+ /**
+ * Tests comparing two {@link Range}s to each other.
+ */
+ public void testEqualsRange() {
+ final Range range1 = createRange(1, 5);
+ Range range2 = createRange(1, 5);
+ assertTrue("ranges not equal", range1.equals((Object) range2));
+ assertTrue("ranges not equal", range2.equals((Object) range1));
+ assertEquals("hash codes not equal", range1.hashCode(), range2.hashCode());
+
+ range2 = createRange(0, 5);
+ assertFalse("ranges equal", range1.equals((Object) range2));
+ assertFalse("ranges equal", range2.equals((Object) range1));
+ assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+
+ range2 = createRange(1, 6);
+ assertFalse("ranges equal", range1.equals((Object) range2));
+ assertFalse("ranges equal", range2.equals((Object) range1));
+ assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+
+ range2 = createRange(0, 6);
+ assertFalse("ranges equal", range1.equals((Object) range2));
+ assertFalse("ranges equal", range2.equals((Object) range1));
+ assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+
+ range2 = createRange(2, 4);
+ assertFalse("ranges equal", range1.equals((Object) range2));
+ assertFalse("ranges equal", range2.equals((Object) range1));
+ assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+
+ range2 = createRange(5, 1);
+ assertFalse("ranges equal", range1.equals((Object) range2));
+ assertFalse("ranges equal", range2.equals((Object) range1));
+ assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+ }
+
+ /**
+ * Tests toString and inspect
+ */
+ public void testToStringAndInspect() {
+ Range range = createRange(1, 5);
+ String expected = range.getFrom() + ".." + range.getTo();
+ assertEquals("wrong string representation", expected, range.toString());
+ assertEquals("wrong string representation", expected, range.inspect());
+
+ range = createRange(5, 1);
+ expected = range.getTo() + ".." + range.getFrom();
+ assertEquals("wrong string representation", expected, range.toString());
+ assertEquals("wrong string representation", expected, range.inspect());
+ }
+
+ /**
+ * Tests getFrom and getTo.
+ */
+ public void testGetFromAndTo() {
+ final int from = 1, to = 5;
+ final Range range = createRange(from, to);
+
+ assertEquals("wrong 'from' value", createValue(from), range.getFrom());
+ assertEquals("wrong 'to' value", createValue(to), range.getTo());
+ }
+
+ /**
+ * Tests comparing a {@link Range} to null.
+ */
+ public void testEqualsNull() {
+ final Range range = createRange(1, 5);
+ assertFalse("range equal to null", range.equals(null));
+ assertFalse("range equal to null Object", range.equals((Object) null));
+ assertFalse("range equal to null Range", range.equals((Range) null));
+ assertFalse("range equal to null List", range.equals((List) null));
+ }
+
+ /**
+ * Tests attempting to add a value to a range.
+ */
+ public void testAddValue() {
+ try {
+ final Range range = createRange(1, 5);
+ range.add(createValue(20));
+ fail("expected exception not thrown");
+ }
+ catch (UnsupportedOperationException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ }
+
+ /**
+ * Tests attempting to remove a value from a range.
+ */
+ public void testRemoveValue() {
+ try {
+ final Range range = createRange(1, 5);
+ range.remove(0);
+ fail("expected exception not thrown");
+ }
+ catch (UnsupportedOperationException e) {
+ assertTrue("expected exception thrown", true);
+ }
+ }
+
+ private void doTestContains(int from, int to, Range range) {
+ // test integers
+ assertTrue("missing 'from' value", range.contains(createValue(from)));
+ assertTrue("missing 'to' value", range.contains(createValue(to)));
+ assertTrue("missing mid point", range.contains(createValue((from + to) / 2)));
+ assertFalse("contains out of range value", range.contains(createValue(from - 1)));
+ assertFalse("contains out of range value", range.contains(createValue(to + 1)));
+
+ // test ranges
+ assertTrue("missing same range", range.containsAll(createRange(from, to)));
+ assertTrue("missing same range", range.containsAll(createRange(to, from)));
+ assertTrue("missing strict subset", range.containsAll(createRange(from + 1, to - 1)));
+ assertTrue("missing subset", range.containsAll(createRange(from, to - 1)));
+ assertTrue("missing subset", range.containsAll(createRange(from + 1, to)));
+ assertFalse("contains non-subset", range.containsAll(createRange(from - 1, to)));
+ assertFalse("contains non-subset", range.containsAll(createRange(from, to + 1)));
+ assertFalse("contains non-subset", range.containsAll(createRange(from - 2, from - 1)));
+
+ // ranges don't contain other ranges
+ assertFalse("range contains sub-range", range.contains(createRange(from + 1, to - 1)));
+
+ // test list
+ final List list = new ArrayList();
+ list.add(createValue(from));
+ list.add(createValue(to));
+ assertTrue("missing strict subset", range.containsAll(list));
+
+ // test non-integer number
+ assertFalse("contains Float", range.contains(new Float((to + from) / 2.0 + 0.3)));
+ }
+
+ /**
+ * Tests whether the range contains a {@link Comparable} object which is not comparable with a {@link Number}.
+ */
+ public void testContainsIncompatibleComparable() {
+ final Range range = createRange(1, 5);
+ assertFalse("range contains string", range.contains("hello"));
+ assertFalse("range contains string", range.contains("1"));
+ }
+
+ /**
+ * Tests whether the range contains a non-comparable object.
+ */
+ public void testContainsNonComparable() {
+ final Range range = createRange(1, 5);
+ assertFalse("range contains hash map", range.contains(new HashMap()));
+ }
+
+ /**
+ * Tests whether a {@link Range} contains another {@link Range} or a specific integer.
+ */
+ public void testContains() {
+ final int from = 1, to = 5;
+ doTestContains(from, to, createRange(from, to));
+ doTestContains(from, to, createRange(to, from));
+ }
+
+ /**
+ * Tests get from a reversed range.
+ */
+ public void testGetFromReversedRange() {
+ final Range range = createRange(5, 1);
+
+ for (int i = 0; i < 5; i++) {
+ assertEquals("wrong element at position " + i, createValue(5 - i), range.get(i));
+ }
+ }
+
+ /**
+ * Tests getting values from the range.
+ */
+ public void testGet() {
+ final Range range = createRange(10, 20);
+ for (int i = 0; i <= 10; i++) {
+ assertEquals("Item at index: " + i, createValue(i + 10), range.get(i));
+ }
+ }
+}
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/RangeTestSuite.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/RangeTestSuite.java (revision 0)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/RangeTestSuite.java (revision 0)
@@ -0,0 +1,40 @@
+/**
+ *
+ */
+package groovy.lang;
+
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+/**
+ * Calls all the range-related tests.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public final class RangeTestSuite extends TestSuite {
+
+ /**
+ * Creates a new {@link RangeTestSuite}
+ */
+ public RangeTestSuite() {
+ addTestSuite(IntRangeTest.class);
+ addTestSuite(ShortRangeTest.class);
+ addTestSuite(IntegerRangeTest.class);
+ addTestSuite(LongRangeTest.class);
+ addTestSuite(FloatRangeTest.class);
+ addTestSuite(BigDecimalRangeTest.class);
+ addTestSuite(CharacterRangeTest.class);
+ addTestSuite(RangeTest.class);
+ }
+
+ /**
+ * Runs the tests in the {@link TestRunner}.
+ *
+ * @param argv
+ * not used
+ */
+ public static void main(String[] argv) {
+ junit.textui.TestRunner.run(new RangeTestSuite());
+ }
+}
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/IntRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/IntRangeTest.java (revision 4080)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/IntRangeTest.java (working copy)
@@ -45,106 +45,59 @@
*/
package groovy.lang;
-import java.util.List;
-import junit.framework.TestCase;
-
/**
+ * Provides unit tests for the IntRange class.
+ *
* @author James Strachan
* @version $Revision$
*/
-public class IntRangeTest extends TestCase {
-
- public void testSize() {
- IntRange r = createRange(0, 10);
- assertEquals("Size of " + r, 11, r.size());
- r = createRange(0, 1);
- assertEquals("Size of " + r, 2, r.size());
- r = createRange(0, 0);
- assertEquals("Size of " + r, 1, r.size());
- }
-
- public void testProperties() {
- IntRange r = createRange(0, 10);
- assertEquals("from", 0, r.getFromInt());
- assertEquals("to", 10, r.getToInt());
- }
-
- public void testGet() {
- IntRange r = createRange(10, 20);
- for (int i = 0; i <= 10; i++) {
- Integer value = (Integer) r.get(i);
- assertEquals("Item at index: " + i, i + 10, value.intValue());
- }
- }
-
- public void testGetOutOfRange() {
- IntRange r = createRange(10, 20);
-
+public class IntRangeTest extends NumberRangeTest {
+
+ public void testCreateTooBigRange() {
try {
- r.get(-1);
- fail("Should have thrown IndexOut");
+ createRange(0, Integer.MAX_VALUE);
+ fail("too large range accepted");
}
- catch (IndexOutOfBoundsException e) {
- // worked
+ catch (IllegalArgumentException e) {
+ assertTrue("expected exception thrown", true);
}
+ }
+
+ /**
+ * Tests providing invalid arguments to the protected constructor.
+ */
+ public void testInvalidArgumentsToConstructor() {
try {
- r.get(11);
- fail("Should have thrown IndexOut");
+ new IntRange(2, 1, true);
+ fail("invalid range created");
}
- catch (IndexOutOfBoundsException e) {
- // worked
+ catch (IllegalArgumentException e) {
+ assertTrue("expected exception thrown", true);
}
+ }
+ /**
+ * Tests getting the to and from values as ints.
+ */
+ public void testGetToFromInt() {
+ final int from = 3, to = 7;
+ final IntRange range = new IntRange(from, to);
+ assertEquals("wrong 'from'", from, range.getFromInt());
+ assertEquals("wrong 'to'", to, range.getToInt());
}
-
- public void testContains() {
- IntRange r = createRange(10, 20);
-
- assertTrue("contains 11", r.contains(new Integer(11)));
- assertTrue("contains 10", r.contains(new Integer(10)));
- assertTrue("contains 19", r.contains(new Integer(19)));
- assertTrue("contains 20", r.contains(new Integer(20)));
- assertFalse("contains 9", r.contains(new Integer(9)));
- assertFalse("contains 21", r.contains(new Integer(21)));
- assertFalse("contains 100", r.contains(new Integer(100)));
- assertFalse("contains -1", r.contains(new Integer(-1)));
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Comparable createValue(int value) {
+ return new Integer(value);
}
- public void testSubList() {
- IntRange r = createRange(10, 20);
-
- List s = r.subList(2, 4);
-
- IntRange sr = (IntRange) s;
-
- assertEquals("from", 12, sr.getFromInt());
- assertEquals("to", 13, sr.getToInt());
- assertEquals("size", 2, sr.size());
- }
-
- public void testHashCodeAndEquals() {
- IntRange a = createRange(1, 11);
- IntRange b = createRange(1, 11);
- IntRange c = createRange(2, 11);
-
- assertEquals("hashcode", a.hashCode(), b.hashCode());
- assertTrue("hashcode", a.hashCode() != c.hashCode());
-
- assertEquals("a and b", a, b);
- assertFalse("a != c", a.equals(c));
- }
-
- public void testIterator() {
- }
-
- protected IntRange createRange(int from, int to) {
+ /**
+ * {@inheritDoc}
+ */
+ protected Range createRange(int from, int to) {
return new IntRange(from, to);
}
-
- protected void assertEquals(String msg, int expected, Object value) {
- assertEquals(msg, new Integer(expected), value);
- }
-
-
}
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/CharacterRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/CharacterRangeTest.java (revision 0)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/CharacterRangeTest.java (revision 0)
@@ -0,0 +1,65 @@
+/**
+ *
+ */
+package groovy.lang;
+
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+/**
+ * Provides a few unit tests for {@link ObjectRange}s of {@link Character}s. More tests are needed.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public class CharacterRangeTest extends TestCase {
+ /**
+ * The range to test.
+ */
+ private ObjectRange range = null;
+
+ /**
+ * The first character in the range.
+ */
+ private final Character FROM = new Character('a');
+
+ /**
+ * The last character in the range.
+ */
+ private final Character TO = new Character('d');
+
+ /**
+ * {@inheritDoc}
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ range = new ObjectRange(FROM, TO);
+ }
+
+ /**
+ * Tests iterating through the range.
+ */
+ public void testIterate() {
+ Iterator iter = range.iterator();
+ assertEquals("wrong first value", FROM, iter.next());
+ for (int expected = FROM.charValue() + 1; expected <= TO.charValue(); expected++) {
+ assertEquals("wrong value", new Integer(expected), iter.next());
+ }
+ }
+
+ /**
+ * Tests getting the 'from' value.
+ */
+ public void testGetFrom() {
+ assertEquals("wrong 'from' value", FROM, range.getFrom());
+ }
+
+ /**
+ * Tests getting the 'to' value.
+ */
+ public void testGetTo() {
+ assertEquals("wrong 'to' value", TO, range.getTo());
+ }
+
+}
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/IntegerRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/IntegerRangeTest.java (revision 0)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/IntegerRangeTest.java (revision 0)
@@ -0,0 +1,71 @@
+/*
+ $Id: IntegerRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Codehaus. For written permission,
+ please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+ nor may "groovy" appear in their names without prior written
+ permission of The Codehaus. "groovy" is a registered
+ trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+ http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+
+/**
+ * Tests {@link ObjectRange}s of {@link Integer}s.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public class IntegerRangeTest extends NumberRangeTest {
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Range createRange(int from, int to) {
+ return new ObjectRange(new Integer(from), new Integer(to));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Comparable createValue(int value) {
+ return new Integer(value);
+ }
+
+}
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/ShortRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/ShortRangeTest.java (revision 0)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/ShortRangeTest.java (revision 0)
@@ -0,0 +1,70 @@
+/*
+ $Id: ShortRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Codehaus. For written permission,
+ please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+ nor may "groovy" appear in their names without prior written
+ permission of The Codehaus. "groovy" is a registered
+ trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+ http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+
+/**
+ * Tests {@link ObjectRange}s of {@link Short}s.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public class ShortRangeTest extends NumberRangeTest {
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Range createRange(int from, int to) {
+ return new ObjectRange(new Short((short) from), new Short((short) to));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Comparable createValue(int value) {
+ return new Integer(value);
+ }
+}
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/LongRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/LongRangeTest.java (revision 0)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/LongRangeTest.java (revision 0)
@@ -0,0 +1,71 @@
+/*
+ $Id: LongRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Codehaus. For written permission,
+ please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+ nor may "groovy" appear in their names without prior written
+ permission of The Codehaus. "groovy" is a registered
+ trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+ http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+
+/**
+ * Tests {@link ObjectRange}s of {@link Long}s.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public class LongRangeTest extends NumberRangeTest {
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Range createRange(int from, int to) {
+ return new ObjectRange(new Long(from), new Long(to));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Comparable createValue(int value) {
+ return new Long(value);
+ }
+
+}
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/FloatRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/FloatRangeTest.java (revision 0)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/FloatRangeTest.java (revision 0)
@@ -0,0 +1,71 @@
+/*
+ $Id: FloatRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Codehaus. For written permission,
+ please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+ nor may "groovy" appear in their names without prior written
+ permission of The Codehaus. "groovy" is a registered
+ trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+ http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+
+/**
+ * Tests {@link ObjectRange}s of {@link Float}s.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public class FloatRangeTest extends NumberRangeTest {
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Range createRange(int from, int to) {
+ return new ObjectRange(new Float(from), new Float(to));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Comparable createValue(int value) {
+ return new Double(value);
+ }
+
+}
Index: /Users/edwin/workspace/groovy/src/test/groovy/lang/BigDecimalRangeTest.java
===================================================================
--- /Users/edwin/workspace/groovy/src/test/groovy/lang/BigDecimalRangeTest.java (revision 0)
+++ /Users/edwin/workspace/groovy/src/test/groovy/lang/BigDecimalRangeTest.java (revision 0)
@@ -0,0 +1,72 @@
+/*
+ $Id: BigDecimalRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of The Codehaus. For written permission,
+ please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+ nor may "groovy" appear in their names without prior written
+ permission of The Codehaus. "groovy" is a registered
+ trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+ http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+import java.math.BigDecimal;
+
+/**
+ * Tests {@link ObjectRange}s of {@link BigDecimal}s.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public class BigDecimalRangeTest extends NumberRangeTest {
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Range createRange(int from, int to) {
+ return new ObjectRange(new BigDecimal(from), new BigDecimal(to));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Comparable createValue(int value) {
+ return new BigDecimal(value);
+ }
+
+}
Index: /Users/edwin/workspace/groovy/src/main/groovy/lang/Range.java
===================================================================
--- /Users/edwin/workspace/groovy/src/main/groovy/lang/Range.java (revision 4080)
+++ /Users/edwin/workspace/groovy/src/main/groovy/lang/Range.java (working copy)
@@ -55,27 +55,54 @@
* @version $Revision$
*/
public interface Range extends List {
-
-
/**
- * @return the lower value in the range
+ * Gets the lower value in the range.
+ *
+ * @return the lower value in the range.
*/
public Comparable getFrom();
/**
+ * Gets the lower value in the range.
+ *
* @return the upper value in the range
*/
public Comparable getTo();
/**
- * @return true if this is a reverse range, iterating backwards
+ * Indicates whether this is a reverse range which iterates backwards
* starting from the to value and ending on the from value
+ *
+ * @return true if this is a reverse range
*/
public boolean isReverse();
+
+ /**
+ * Steps through the range, calling a closure for each number.
+ *
+ * @param step
+ * the amount by which to step. If negative, steps through the
+ * range backwards.
+ * @param closure
+ * the {@link Closure} to call
+ */
+ public void step(int step, Closure closure);
+
+ /**
+ * Forms a list by stepping through the range by the indicated interval.
+ *
+ * @param step
+ * the amount by which to step. If negative, steps through the
+ * range backwards.
+ *
+ * @return the list formed by stepping through the range by the indicated
+ * interval.
+ */
+ public List step(int step);
/**
- * @return the verbose String representation of this Range as would be typed into a console
- * to create the Range instance
+ * @return the verbose {@link String} representation of this {@link Range} as would be typed into a console
+ * to create the {@link Range} instance
*/
public String inspect();
}