001 /* 002 $Id: ObjectRange.java,v 1.14 2005/01/13 13:09:17 blackdrag Exp $ 003 004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 006 Redistribution and use of this software and associated documentation 007 ("Software"), with or without modification, are permitted provided 008 that the following conditions are met: 009 010 1. Redistributions of source code must retain copyright 011 statements and notices. Redistributions must also contain a 012 copy of this document. 013 014 2. Redistributions in binary form must reproduce the 015 above copyright notice, this list of conditions and the 016 following disclaimer in the documentation and/or other 017 materials provided with the distribution. 018 019 3. The name "groovy" must not be used to endorse or promote 020 products derived from this Software without prior written 021 permission of The Codehaus. For written permission, 022 please contact info@codehaus.org. 023 024 4. Products derived from this Software may not be called "groovy" 025 nor may "groovy" appear in their names without prior written 026 permission of The Codehaus. "groovy" is a registered 027 trademark of The Codehaus. 028 029 5. Due credit should be given to The Codehaus - 030 http://groovy.codehaus.org/ 031 032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS 033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 043 OF THE POSSIBILITY OF SUCH DAMAGE. 044 045 */ 046 package groovy.lang; 047 048 import org.codehaus.groovy.runtime.InvokerHelper; 049 import org.codehaus.groovy.runtime.IteratorClosureAdapter; 050 051 import java.util.AbstractList; 052 import java.util.Iterator; 053 import java.util.List; 054 055 /** 056 * Represents an inclusive list of objects from a value to a value using 057 * comparators 058 * 059 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 060 * @version $Revision: 1.14 $ 061 */ 062 public class ObjectRange extends AbstractList implements Range { 063 064 private Comparable from; 065 private Comparable to; 066 private int size = -1; 067 private final boolean reverse; 068 069 public ObjectRange(Comparable from, Comparable to) { 070 this.reverse = InvokerHelper.compareGreaterThan(from, to); 071 if (this.reverse) { 072 constructorHelper(to, from); 073 } else { 074 constructorHelper(from, to); 075 } 076 } 077 078 public ObjectRange(Comparable from, Comparable to, boolean reverse) { 079 constructorHelper(from, to); 080 081 this.reverse = reverse; 082 } 083 084 private void constructorHelper(Comparable from, Comparable to) { 085 if (from == null) { 086 throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range"); 087 } 088 if (to == null) { 089 throw new IllegalArgumentException("Must specify a non-null value for the 'to' index in a Range"); 090 } 091 if (from.getClass() == to.getClass()) { 092 this.from = from; 093 this.to = to; 094 } else { 095 this.from = normaliseType(from); 096 this.to = normaliseType(to); 097 } 098 } 099 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 }