View Javadoc

1   /*
2    $Id: ObjectRange.java,v 1.12 2004/12/27 10:31:24 spullara Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
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             // lets lazily calculate the size
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 }