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 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
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 }