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 }