1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package groovy.lang;
47
48 import org.codehaus.groovy.runtime.InvokerHelper;
49 import org.codehaus.groovy.runtime.IteratorClosureAdapter;
50
51 import java.util.AbstractList;
52 import java.util.Iterator;
53 import java.util.List;
54 import java.math.BigDecimal;
55 import java.math.BigInteger;
56
57 /***
58 * Represents an inclusive list of objects from a value to a value using
59 * comparators
60 *
61 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
62 * @version $Revision: 1.15 $
63 */
64 public class ObjectRange extends AbstractList implements Range {
65
66 private Comparable from;
67 private Comparable to;
68 private int size = -1;
69 private final boolean reverse;
70
71 public ObjectRange(Comparable from, Comparable to) {
72 this.reverse = InvokerHelper.compareGreaterThan(from, to);
73 if (this.reverse) {
74 constructorHelper(to, from);
75 } else {
76 constructorHelper(from, to);
77 }
78 }
79
80 public ObjectRange(Comparable from, Comparable to, boolean reverse) {
81 constructorHelper(from, to);
82
83 this.reverse = reverse;
84 }
85
86 private void constructorHelper(Comparable from, Comparable to) {
87 if (from == null) {
88 throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range");
89 }
90 if (to == null) {
91 throw new IllegalArgumentException("Must specify a non-null value for the 'to' index in a Range");
92 }
93 if (from.getClass() == to.getClass()) {
94 this.from = from;
95 this.to = to;
96 } else {
97 this.from = normaliseType(from);
98 this.to = normaliseType(to);
99 }
100 }
101
102 public int hashCode() {
103 /*** @todo should code this the Josh Bloch way */
104 return from.hashCode() ^ to.hashCode() + (reverse ? 1 : 0);
105 }
106
107 public boolean equals(Object that) {
108 if (that instanceof ObjectRange) {
109 return equals((ObjectRange) that);
110 } else if (that instanceof List) {
111 return equals((List) that);
112 }
113 return false;
114 }
115
116 public boolean equals(ObjectRange that) {
117 return this.reverse == that.reverse
118 && InvokerHelper.compareEqual(this.from, that.from)
119 && InvokerHelper.compareEqual(this.to, that.to);
120 }
121
122 public boolean equals(List that) {
123 int size = size();
124 if (that.size() == size) {
125 for (int i = 0; i < size; i++) {
126 if (!InvokerHelper.compareEqual(get(i), that.get(i))) {
127 return false;
128 }
129 }
130 return true;
131 }
132 return false;
133 }
134
135 public Comparable getFrom() {
136 return from;
137 }
138
139 public Comparable getTo() {
140 return to;
141 }
142
143 public boolean isReverse() {
144 return reverse;
145 }
146
147 public Object get(int index) {
148 if (index < 0) {
149 throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
150 }
151 if (index >= size()) {
152 throw new IndexOutOfBoundsException("Index: " + index + " is too big for range: " + this);
153 }
154 Object value = null;
155 if (reverse) {
156 value = to;
157
158 for (int i = 0; i < index; i++) {
159 value = decrement(value);
160 }
161 } else {
162 value = from;
163 for (int i = 0; i < index; i++) {
164 value = increment(value);
165 }
166 }
167 return value;
168 }
169
170 public Iterator iterator() {
171 return new Iterator() {
172 int index = 0;
173 Object value = (reverse) ? to : from;
174
175 public boolean hasNext() {
176 return index < size();
177 }
178
179 public Object next() {
180 if (index++ > 0) {
181 if (index > size()) {
182 value = null;
183 } else {
184 if (reverse) {
185 value = decrement(value);
186 } else {
187 value = increment(value);
188 }
189 }
190 }
191 return value;
192 }
193
194 public void remove() {
195 ObjectRange.this.remove(index);
196 }
197 };
198 }
199
200 public int size() {
201 if (size == -1) {
202 if (from instanceof Integer && to instanceof Integer) {
203
204 size = 0;
205 int fromNum = ((Integer) from).intValue();
206 int toNum = ((Integer) to).intValue();
207 size = toNum - fromNum + 1;
208 }
209 else if (from instanceof BigDecimal || to instanceof BigDecimal) {
210
211 size = 0;
212 BigDecimal fromNum = new BigDecimal("" + from);
213 BigDecimal toNum = new BigDecimal("" + to);
214 BigInteger sizeNum = toNum.subtract(fromNum).add(new BigDecimal(1.0)).toBigInteger();
215 size = sizeNum.intValue();
216 }
217 else {
218
219 size = 0;
220 Object value = from;
221 while (to.compareTo(value) >= 0) {
222 value = increment(value);
223 size++;
224 }
225 }
226 }
227 return size;
228 }
229
230 public List subList(int fromIndex, int toIndex) {
231 if (fromIndex < 0) {
232 throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
233 }
234 int size = size();
235 if (toIndex > size) {
236 throw new IndexOutOfBoundsException("toIndex = " + toIndex);
237 }
238 if (fromIndex > toIndex) {
239 throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
240 }
241 if (--toIndex >= size) {
242 return new ObjectRange((Comparable) get(fromIndex), getTo(), reverse);
243 } else {
244 return new ObjectRange((Comparable) get(fromIndex), (Comparable) get(toIndex), reverse);
245 }
246 }
247
248 public String toString() {
249 return (reverse) ? "" + to + ".." + from : "" + from + ".." + to;
250 }
251
252 public String inspect() {
253 String toText = InvokerHelper.inspect(to);
254 String fromText = InvokerHelper.inspect(from);
255 return (reverse) ? "" + toText + ".." + fromText : "" + fromText + ".." + toText;
256 }
257
258 public boolean contains(Comparable value) {
259 if (from instanceof BigDecimal || to instanceof BigDecimal) {
260 int result = (new BigDecimal("" + from)).compareTo(new BigDecimal("" + value));
261 if (result == 0) {
262 return true;
263 }
264 return result < 0 && (new BigDecimal("" + to)).compareTo(new BigDecimal("" + value)) >= 0;
265 }
266 else {
267 int result = from.compareTo(value);
268 if (result == 0) {
269 return true;
270 }
271 return result < 0 && to.compareTo(value) >= 0;
272 }
273 }
274
275 public void step(int step, Closure closure) {
276 if (reverse) {
277 step = -step;
278 }
279 if (step >= 0) {
280 Comparable value = from;
281 while (value.compareTo(to) <= 0) {
282 closure.call(value);
283 for (int i = 0; i < step; i++) {
284 value = (Comparable) increment(value);
285 }
286 }
287 } else {
288 step = -step;
289 Comparable value = to;
290 while (value.compareTo(from) >= 0) {
291 closure.call(value);
292 for (int i = 0; i < step; i++) {
293 value = (Comparable) decrement(value);
294 }
295 }
296 }
297 }
298
299 public List step(int step) {
300 IteratorClosureAdapter adapter = new IteratorClosureAdapter(this);
301 step(step, adapter);
302 return adapter.asList();
303 }
304
305 protected Object increment(Object value) {
306 return InvokerHelper.invokeMethod(value, "next", null);
307 }
308
309 protected Object decrement(Object value) {
310 return InvokerHelper.invokeMethod(value, "previous", null);
311 }
312
313 private static Comparable normaliseType(final Comparable operand) {
314 if (operand instanceof Character) {
315 return new Integer(((Character) operand).charValue());
316 } else if (operand instanceof String) {
317 final String string = (String) operand;
318
319 if (string.length() == 1)
320 return new Integer(string.charAt(0));
321 else
322 return string;
323 } else {
324 return operand;
325 }
326 }
327 }