View Javadoc

    1   /*
    2    $Id: ObjectRange.java 4290 2006-12-01 20:28:08Z paulk $
    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  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             // this test depends deeply on the String.next implementation
    106             // 009.next is 00:, not 010
    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                 // lets fast calculate the size
    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                 // lets fast calculate the size
    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                 // lets lazily calculate the size
    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 }