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.16 $
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 if (from instanceof String || to instanceof String) {
101
102
103 String start = from.toString();
104 String end = to.toString();
105 if (start.length()>end.length()){
106 throw new IllegalArgumentException("Incompatible Strings for Range: starting String is longer than ending string");
107 }
108 int length = Math.min(start.length(),end.length());
109 int i = 0;
110 for (i=0; i<length; i++) {
111 if (start.charAt(i) != end.charAt(i)) break;
112 }
113 if (i<length-1) {
114 throw new IllegalArgumentException("Incompatible Strings for Range: String#next() will not reach the expected value");
115 }
116
117 }
118 }
119
120 public int hashCode() {
121 /*** @todo should code this the Josh Bloch way */
122 return from.hashCode() ^ to.hashCode() + (reverse ? 1 : 0);
123 }
124
125 public boolean equals(Object that) {
126 if (that instanceof ObjectRange) {
127 return equals((ObjectRange) that);
128 } else if (that instanceof List) {
129 return equals((List) that);
130 }
131 return false;
132 }
133
134 public boolean equals(ObjectRange that) {
135 return this.reverse == that.reverse
136 && InvokerHelper.compareEqual(this.from, that.from)
137 && InvokerHelper.compareEqual(this.to, that.to);
138 }
139
140 public boolean equals(List that) {
141 int size = size();
142 if (that.size() == size) {
143 for (int i = 0; i < size; i++) {
144 if (!InvokerHelper.compareEqual(get(i), that.get(i))) {
145 return false;
146 }
147 }
148 return true;
149 }
150 return false;
151 }
152
153 public Comparable getFrom() {
154 return from;
155 }
156
157 public Comparable getTo() {
158 return to;
159 }
160
161 public boolean isReverse() {
162 return reverse;
163 }
164
165 public Object get(int index) {
166 if (index < 0) {
167 throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
168 }
169 if (index >= size()) {
170 throw new IndexOutOfBoundsException("Index: " + index + " is too big for range: " + this);
171 }
172 Object value = null;
173 if (reverse) {
174 value = to;
175
176 for (int i = 0; i < index; i++) {
177 value = decrement(value);
178 }
179 } else {
180 value = from;
181 for (int i = 0; i < index; i++) {
182 value = increment(value);
183 }
184 }
185 return value;
186 }
187
188 public Iterator iterator() {
189 return new Iterator() {
190 int index = 0;
191 Object value = (reverse) ? to : from;
192
193 public boolean hasNext() {
194 return index < size();
195 }
196
197 public Object next() {
198 if (index++ > 0) {
199 if (index > size()) {
200 value = null;
201 } else {
202 if (reverse) {
203 value = decrement(value);
204 } else {
205 value = increment(value);
206 }
207 }
208 }
209 return value;
210 }
211
212 public void remove() {
213 ObjectRange.this.remove(index);
214 }
215 };
216 }
217
218 public int size() {
219 if (size == -1) {
220 if (from instanceof Integer && to instanceof Integer) {
221
222 size = 0;
223 int fromNum = ((Integer) from).intValue();
224 int toNum = ((Integer) to).intValue();
225 size = toNum - fromNum + 1;
226 }
227 else if (from instanceof BigDecimal || to instanceof BigDecimal) {
228
229 size = 0;
230 BigDecimal fromNum = new BigDecimal("" + from);
231 BigDecimal toNum = new BigDecimal("" + to);
232 BigInteger sizeNum = toNum.subtract(fromNum).add(new BigDecimal(1.0)).toBigInteger();
233 size = sizeNum.intValue();
234 }
235 else {
236
237 size = 0;
238 Object value = from;
239 while (to.compareTo(value) >= 0) {
240 value = increment(value);
241 size++;
242 }
243 }
244 }
245 return size;
246 }
247
248 public List subList(int fromIndex, int toIndex) {
249 if (fromIndex < 0) {
250 throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
251 }
252 int size = size();
253 if (toIndex > size) {
254 throw new IndexOutOfBoundsException("toIndex = " + toIndex);
255 }
256 if (fromIndex > toIndex) {
257 throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
258 }
259 if (--toIndex >= size) {
260 return new ObjectRange((Comparable) get(fromIndex), getTo(), reverse);
261 } else {
262 return new ObjectRange((Comparable) get(fromIndex), (Comparable) get(toIndex), reverse);
263 }
264 }
265
266 public String toString() {
267 return (reverse) ? "" + to + ".." + from : "" + from + ".." + to;
268 }
269
270 public String inspect() {
271 String toText = InvokerHelper.inspect(to);
272 String fromText = InvokerHelper.inspect(from);
273 return (reverse) ? "" + toText + ".." + fromText : "" + fromText + ".." + toText;
274 }
275
276 public boolean contains(Comparable value) {
277 if (from instanceof BigDecimal || to instanceof BigDecimal) {
278 int result = (new BigDecimal("" + from)).compareTo(new BigDecimal("" + value));
279 if (result == 0) {
280 return true;
281 }
282 return result < 0 && (new BigDecimal("" + to)).compareTo(new BigDecimal("" + value)) >= 0;
283 }
284 else {
285 int result = from.compareTo(value);
286 if (result == 0) {
287 return true;
288 }
289 return result < 0 && to.compareTo(value) >= 0;
290 }
291 }
292
293 public void step(int step, Closure closure) {
294 if (reverse) {
295 step = -step;
296 }
297 if (step >= 0) {
298 Comparable value = from;
299 while (value.compareTo(to) <= 0) {
300 closure.call(value);
301 for (int i = 0; i < step; i++) {
302 value = (Comparable) increment(value);
303 }
304 }
305 } else {
306 step = -step;
307 Comparable value = to;
308 while (value.compareTo(from) >= 0) {
309 closure.call(value);
310 for (int i = 0; i < step; i++) {
311 value = (Comparable) decrement(value);
312 }
313 }
314 }
315 }
316
317 public List step(int step) {
318 IteratorClosureAdapter adapter = new IteratorClosureAdapter(this);
319 step(step, adapter);
320 return adapter.asList();
321 }
322
323 protected Object increment(Object value) {
324 return InvokerHelper.invokeMethod(value, "next", null);
325 }
326
327 protected Object decrement(Object value) {
328 return InvokerHelper.invokeMethod(value, "previous", null);
329 }
330
331 private static Comparable normaliseType(final Comparable operand) {
332 if (operand instanceof Character) {
333 return new Integer(((Character) operand).charValue());
334 } else if (operand instanceof String) {
335 final String string = (String) operand;
336
337 if (string.length() == 1)
338 return new Integer(string.charAt(0));
339 else
340 return string;
341 } else {
342 return operand;
343 }
344 }
345 }