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(); }