View Javadoc

1   /*
2    $Id: ObjectRange.java,v 1.11 2004/03/10 13:49:50 jstrachan Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package groovy.lang;
47  
48  import java.util.AbstractList;
49  import java.util.Iterator;
50  import java.util.List;
51  
52  import org.codehaus.groovy.runtime.InvokerHelper;
53  import org.codehaus.groovy.runtime.IteratorClosureAdapter;
54  
55  /***
56   * Represents an inclusive list of objects from a value to a value using
57   * comparators
58   * 
59   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
60   * @version $Revision: 1.11 $
61   */
62  public class ObjectRange extends AbstractList implements Range {
63  
64      private Comparable from;
65      private Comparable to;
66      private int size = -1;
67      private final boolean reverse;
68  
69      public ObjectRange(Comparable from, Comparable to) {
70      	this.reverse = InvokerHelper.compareGreaterThan(from, to);
71      	if (this.reverse) {
72      		constructorHelper(to, from);
73      	} else {
74      		constructorHelper(from, to);
75      	}
76      }
77  
78      public ObjectRange(Comparable from, Comparable to, boolean reverse) {
79      	constructorHelper(from, to);
80      	
81          this.reverse = reverse;
82      }
83      
84      private void constructorHelper(Comparable from, Comparable to) {
85          if (from == null) {
86              throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range");
87          }
88          if (to == null) {
89              throw new IllegalArgumentException("Must specify a non-null value for the 'to' index in a Range");
90          }
91      	if (from.getClass() == to.getClass()) {
92      		this.from = from;
93      		this.to = to;
94      	} else {
95      		this.from = normaliseType(from);
96      		this.to = normaliseType(to);
97      	}
98      }
99  
100     public int hashCode() {
101         /*** @todo should code this the Josh Bloch way */
102         return from.hashCode() ^ to.hashCode() + (reverse ? 1 : 0);
103     }
104 
105     public boolean equals(Object that) {
106         if (that instanceof ObjectRange) {
107             return equals((ObjectRange) that);
108         }
109         else if (that instanceof List) {
110             return equals((List) that);
111         }
112         return false;
113     }
114 
115     public boolean equals(ObjectRange that) {
116         return this.reverse == that.reverse
117             && InvokerHelper.compareEqual(this.from, that.from)
118             && InvokerHelper.compareEqual(this.to, that.to);
119     }
120 
121     public boolean equals(List that) {
122         int size = size();
123         if (that.size() == size) {
124             for (int i = 0; i < size; i++) {
125                 if (!InvokerHelper.compareEqual(get(i), that.get(i))) {
126                     return false;
127                 }
128             }
129             return true;
130         }
131         return false;
132     }
133 
134     public Comparable getFrom() {
135         return from;
136     }
137 
138     public Comparable getTo() {
139         return to;
140     }
141 
142     public boolean isReverse() {
143         return reverse;
144     }
145 
146     public Object get(int index) {
147         if (index < 0) {
148             throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
149         }
150         if (index >= size()) {
151             throw new IndexOutOfBoundsException("Index: " + index + " is too big for range: " + this);
152         }
153         Object value = null;
154         if (reverse) {
155             value = to;
156             System.out.println("get(" + index + ")");
157             
158             for (int i = 0; i < index; i++) {
159                 System.out.println("decrement: " + i + " value: " + value);
160                 value = decrement(value);
161             }
162         }
163         else {
164             value = from;
165             for (int i = 0; i < index; i++) {
166                 value = increment(value);
167             }
168         }
169         return value;
170     }
171 
172     public Iterator iterator() {
173         return new Iterator() {
174             int index = 0;
175             Object value = (reverse) ? to : from;
176 
177             public boolean hasNext() {
178                 return index < size();
179             }
180 
181             public Object next() {
182                 if (index++ > 0) {
183                     if (index > size()) {
184                         value = null;
185                     }
186                     else {
187                         if (reverse) {
188                             value = decrement(value);
189                         }
190                         else {
191                             value = increment(value);
192                         }
193                     }
194                 }
195                 return value;
196             }
197 
198             public void remove() {
199                 ObjectRange.this.remove(index);
200             }
201         };
202     }
203 
204     public int size() {
205         if (size == -1) {
206             // lets lazily calculate the size
207             size = 0;
208             Object value = from;
209             while (to.compareTo(value) >= 0) {
210                 value = increment(value);
211                 size++;
212             }
213         }
214         return size;
215     }
216 
217     public List subList(int fromIndex, int toIndex) {
218         if (fromIndex < 0) {
219             throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
220         }
221         int size = size();
222         if (toIndex > size) {
223             throw new IndexOutOfBoundsException("toIndex = " + toIndex);
224         }
225         if (fromIndex > toIndex) {
226             throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
227         }
228         if (--toIndex >= size) {
229             return new ObjectRange((Comparable) get(fromIndex), getTo(), reverse);
230         }
231         else {
232             return new ObjectRange((Comparable) get(fromIndex), (Comparable) get(toIndex), reverse);
233         }
234     }
235 
236     public String toString() {
237         return (reverse) ? "" + to + ".." + from : "" + from + ".." + to;
238     }
239 
240     public String inspect() {
241         String toText = InvokerHelper.inspect(to);
242         String fromText = InvokerHelper.inspect(from);
243         return (reverse) ? "" + toText + ".." + fromText : "" + fromText + ".." + toText;
244     }
245 
246     public boolean contains(Comparable value) {
247         int result = from.compareTo(value);
248         if (result == 0) {
249             return true;
250         }
251         return result < 0 && to.compareTo(value) >= 0;
252     }
253 
254     public void step(int step, Closure closure) {
255         if (reverse) {
256             step = -step;
257         }
258         if (step >= 0) {
259             Comparable value = from;
260             while (value.compareTo(to) <= 0) {
261                 closure.call(value);
262                 for (int i = 0; i < step; i++) {
263                     value = (Comparable) increment(value);
264                 }
265             }
266         }
267         else {
268             step = -step;
269             Comparable value = to;
270             while (value.compareTo(from) >= 0) {
271                 closure.call(value);
272                 for (int i = 0; i < step; i++) {
273                     value = (Comparable) decrement(value);
274                 }
275             }
276         }
277     }
278 
279     public List step(int step) {
280         IteratorClosureAdapter adapter = new IteratorClosureAdapter(this);
281         step(step, adapter);
282         return adapter.asList();
283     }
284 
285     protected Object increment(Object value) {
286         return InvokerHelper.invokeMethod(value, "next", null);
287     }
288 
289     protected Object decrement(Object value) {
290         return InvokerHelper.invokeMethod(value, "previous", null);
291     }
292     
293     private static Comparable normaliseType(final Comparable operand) {
294     	if (operand instanceof Character) {
295     		return new Integer(((Character)operand).charValue());
296     	} else if (operand instanceof String) {
297     	final String string = (String)operand;
298     	
299     		if (string.length() == 1)
300     			return new Integer(string.charAt(0));
301     		else
302     			return string;
303     	} else {
304     		return operand;
305     	}
306     }
307 }