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