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 }