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
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.12 $
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 } else if (that instanceof List) {
109 return equals((List) that);
110 }
111 return false;
112 }
113
114 public boolean equals(ObjectRange that) {
115 return this.reverse == that.reverse
116 && InvokerHelper.compareEqual(this.from, that.from)
117 && InvokerHelper.compareEqual(this.to, that.to);
118 }
119
120 public boolean equals(List that) {
121 int size = size();
122 if (that.size() == size) {
123 for (int i = 0; i < size; i++) {
124 if (!InvokerHelper.compareEqual(get(i), that.get(i))) {
125 return false;
126 }
127 }
128 return true;
129 }
130 return false;
131 }
132
133 public Comparable getFrom() {
134 return from;
135 }
136
137 public Comparable getTo() {
138 return to;
139 }
140
141 public boolean isReverse() {
142 return reverse;
143 }
144
145 public Object get(int index) {
146 if (index < 0) {
147 throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
148 }
149 if (index >= size()) {
150 throw new IndexOutOfBoundsException("Index: " + index + " is too big for range: " + this);
151 }
152 Object value = null;
153 if (reverse) {
154 value = to;
155
156 for (int i = 0; i < index; i++) {
157 value = decrement(value);
158 }
159 } else {
160 value = from;
161 for (int i = 0; i < index; i++) {
162 value = increment(value);
163 }
164 }
165 return value;
166 }
167
168 public Iterator iterator() {
169 return new Iterator() {
170 int index = 0;
171 Object value = (reverse) ? to : from;
172
173 public boolean hasNext() {
174 return index < size();
175 }
176
177 public Object next() {
178 if (index++ > 0) {
179 if (index > size()) {
180 value = null;
181 } else {
182 if (reverse) {
183 value = decrement(value);
184 } else {
185 value = increment(value);
186 }
187 }
188 }
189 return value;
190 }
191
192 public void remove() {
193 ObjectRange.this.remove(index);
194 }
195 };
196 }
197
198 public int size() {
199 if (size == -1) {
200
201 size = 0;
202 Object value = from;
203 while (to.compareTo(value) >= 0) {
204 value = increment(value);
205 size++;
206 }
207 }
208 return size;
209 }
210
211 public List subList(int fromIndex, int toIndex) {
212 if (fromIndex < 0) {
213 throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
214 }
215 int size = size();
216 if (toIndex > size) {
217 throw new IndexOutOfBoundsException("toIndex = " + toIndex);
218 }
219 if (fromIndex > toIndex) {
220 throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
221 }
222 if (--toIndex >= size) {
223 return new ObjectRange((Comparable) get(fromIndex), getTo(), reverse);
224 } else {
225 return new ObjectRange((Comparable) get(fromIndex), (Comparable) get(toIndex), reverse);
226 }
227 }
228
229 public String toString() {
230 return (reverse) ? "" + to + ".." + from : "" + from + ".." + to;
231 }
232
233 public String inspect() {
234 String toText = InvokerHelper.inspect(to);
235 String fromText = InvokerHelper.inspect(from);
236 return (reverse) ? "" + toText + ".." + fromText : "" + fromText + ".." + toText;
237 }
238
239 public boolean contains(Comparable value) {
240 int result = from.compareTo(value);
241 if (result == 0) {
242 return true;
243 }
244 return result < 0 && to.compareTo(value) >= 0;
245 }
246
247 public void step(int step, Closure closure) {
248 if (reverse) {
249 step = -step;
250 }
251 if (step >= 0) {
252 Comparable value = from;
253 while (value.compareTo(to) <= 0) {
254 closure.call(value);
255 for (int i = 0; i < step; i++) {
256 value = (Comparable) increment(value);
257 }
258 }
259 } else {
260 step = -step;
261 Comparable value = to;
262 while (value.compareTo(from) >= 0) {
263 closure.call(value);
264 for (int i = 0; i < step; i++) {
265 value = (Comparable) decrement(value);
266 }
267 }
268 }
269 }
270
271 public List step(int step) {
272 IteratorClosureAdapter adapter = new IteratorClosureAdapter(this);
273 step(step, adapter);
274 return adapter.asList();
275 }
276
277 protected Object increment(Object value) {
278 return InvokerHelper.invokeMethod(value, "next", null);
279 }
280
281 protected Object decrement(Object value) {
282 return InvokerHelper.invokeMethod(value, "previous", null);
283 }
284
285 private static Comparable normaliseType(final Comparable operand) {
286 if (operand instanceof Character) {
287 return new Integer(((Character) operand).charValue());
288 } else if (operand instanceof String) {
289 final String string = (String) operand;
290
291 if (string.length() == 1)
292 return new Integer(string.charAt(0));
293 else
294 return string;
295 } else {
296 return operand;
297 }
298 }
299 }