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 }