View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2015-2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.util;
18  
19  // Java SE
20  import java.io.Serializable;
21  import java.util.AbstractSet;
22  import java.util.Iterator;
23  import java.util.NoSuchElementException;
24  
25  /**
26   * Exposes a range of integer values as a set. Avoids the allocation of storage for all
27   * values. Order of elements is guaranteed to be in the order from the specified start and
28   * stop values.
29   * <p>
30   * If combination of start/stop/step values are not mathematically possible to represent
31   * as a set of values, it is represented by this implementation as an empty set.
32   */
33  public class RangeSet extends AbstractSet<Integer> implements Cloneable, Serializable {
34  
35      /** Establishes serialized object compatibility. */
36      private static final long serialVersionUID = 1L;
37  
38      /** The start of the range, inclusive. */
39      private final int start;
40  
41      /** The end of the range, inclusive. */
42      private final int stop;
43  
44      /** TODO: Description. */
45      private final int step;
46  
47      /**
48       * Constructs a range set for a sequence of numbers, starting at {@code 0} with
49       * the value to stop.  Equivalent to constructing the range set with:
50       * {@code RangeSet(0, stop, 1)}.
51       * @param stop the point at which to stop the range (exclusive).
52       */
53      public RangeSet(int stop) {
54          this(0, stop, 1);
55      }
56  
57      /**
58       * Constructs a range set for the specified range of integers with a step of {@code 1}.
59       * Equivalent to constructing the range set with: {@code RangeSet(start, stop, 1)}.
60       *
61       * @param start the start of the range (inclusive).
62       * @param stop the point at which to stop the range (exclusive).
63       */
64      public RangeSet(int start, int stop) {
65          this(start, stop, 1);
66      }
67  
68      /**
69       * Constructs a range set for the specified range of integers and increment.
70       *
71       * @param start the start of the range, inclusive.
72       * @param stop the point at which to stop the range (exclusive).
73       * @param step the step to increment for each value in the range.
74       * @throws IllegalArgumentException if {@code step} is {@code 0}.
75       */
76      public RangeSet(int start, int stop, int step) {
77          if (step == 0) {
78              throw new IllegalArgumentException();
79          }
80          this.start = start;
81          this.stop = stop;
82          this.step = step;
83      }
84  
85      /**
86       * Returns the number of elements in this set.
87       */
88      @Override
89      public int size() {
90          int difference = (stop - start); // show all work
91          int count = (difference / step);
92          int remainder = Math.abs(difference % step);
93          return (count > 0 ? count + remainder : 0);
94      }
95  
96      /**
97       * Returns {@code true} if this set contains no elements.
98       */
99      @Override
100     public boolean isEmpty() {
101         return (size() == 0);
102     }
103 
104     /**
105      * Returns {@code true} if this set contains the specified element.
106      *
107      * @param o element whose presence in this set is to be tested.
108      * @return {@code true} if this set contains the specified element.
109      */
110     @Override
111     public boolean contains(Object o) {
112         boolean result = false;
113         if (o != null && o instanceof Integer && size() != 0) {
114             int contains = ((Number) o).intValue();
115             if ((step > 0 && contains >= start && contains < stop)
116                     || (step < 0 && contains >= start && contains > stop)) {
117                 result = ((contains - start) % step == 0);
118             }
119         }
120         return result;
121     }
122 
123     /**
124      * Returns an iterator over the elements in this set. The elements are returned in
125      * the order they are specified from start to stop.
126      */
127     @Override
128     public Iterator<Integer> iterator() {
129         return new Iterator<Integer>() {
130             int cursor = start;
131             @Override public boolean hasNext() {
132                 boolean result;
133                 if (step > 0) {
134                     result = (cursor < stop);
135                 } else {
136                     result = (cursor > stop);
137                 }
138                 return result;
139             }
140             @Override public Integer next() {
141                 if (!hasNext()) {
142                     throw new NoSuchElementException();
143                 }
144                 int result = cursor;
145                 cursor += step;
146                 return result;
147             }
148             @Override public void remove() {
149                 throw new UnsupportedOperationException();
150             }
151         };
152     }
153 }