1 /*** 2 * Ambient - A music player for the Android platform 3 Copyright (C) 2007 Martin Vysny 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 package sk.baka.ambient.commons; 19 20 import java.util.Iterator; 21 22 /*** 23 * Specifies an integer index interval. Immutable. Iterator simply iterates over 24 * indices contained in this interval. 25 * 26 * @author Martin Vysny 27 */ 28 public final class Interval implements Iterable<Integer> { 29 /*** 30 * Index of first item in the interval. 31 */ 32 public final int start; 33 34 /*** 35 * Number of successive items in the interval. 36 */ 37 public final int length; 38 39 /*** 40 * Checks if at least a single item is in the interval. 41 * 42 * @return <code>false</code> if at least single item is in the interval, 43 * <code>true</code> otherwise. 44 */ 45 public boolean isEmpty() { 46 return length == 0; 47 } 48 49 /*** 50 * Checks if given item is contained in the interval. 51 * 52 * @param index 53 * index of item 54 * @return <code>true</code> if the item belongs to the interval. 55 */ 56 public boolean contains(int index) { 57 return (index >= start) && (index <= end); 58 } 59 60 /*** 61 * Checks if given interval is contained in the interval. 62 * 63 * @param other 64 * the other interval 65 * @return <code>true</code> if given interval belongs to the interval. 66 */ 67 public boolean contains(final Interval other) { 68 return (other.start >= start) && (other.end <= end); 69 } 70 71 /*** 72 * Creates new interval. 73 * 74 * @param start 75 * Index of first item in the interval. 76 * @param length 77 * Number of successive items in the interval. 78 */ 79 public Interval(final int start, final int length) { 80 super(); 81 if (length < 0) 82 throw new IllegalArgumentException("length must not be negative"); 83 if (start < 0) 84 throw new IllegalArgumentException("start must not be negative"); 85 this.start = start; 86 this.length = length; 87 this.end = start + length - 1; 88 } 89 90 /*** 91 * An empty interval. 92 */ 93 public static final Interval EMPTY = new Interval(0, 0); 94 95 /*** 96 * Creates given interval. The interval includes both endpoints. 97 * 98 * @param intervalStart 99 * start of interval 100 * @param intervalEnd 101 * end of interval 102 * @return interval which includes both endpoints. 103 */ 104 public static Interval fromRange(int intervalStart, int intervalEnd) { 105 if (intervalEnd < intervalStart) { 106 return new Interval(intervalEnd, intervalStart - intervalEnd + 1); 107 } 108 return new Interval(intervalStart, intervalEnd - intervalStart + 1); 109 } 110 111 /*** 112 * Returns interval which contains only a single item. 113 * 114 * @param itemIndex 115 * the item index. 116 * @return interval containing only a single item. 117 */ 118 public static Interval fromItem(final int itemIndex) { 119 return fromRange(itemIndex, itemIndex); 120 } 121 122 @Override 123 public boolean equals(Object o) { 124 if (!(o instanceof Interval)) 125 return false; 126 final Interval other = (Interval) o; 127 if (isEmpty() && other.isEmpty()) 128 return true; 129 return (start == other.start) && (length == other.length); 130 } 131 132 @Override 133 public int hashCode() { 134 if (length == 0) 135 return 0; 136 int hashcode = start; 137 hashcode = hashcode * 4107 + length; 138 return hashcode; 139 } 140 141 @Override 142 public String toString() { 143 if (isEmpty()) 144 return "<empty>"; 145 return "<" + start + ".." + end + ">"; 146 } 147 148 /*** 149 * Index of last item in the interval. 150 */ 151 public final int end; 152 153 public Iterator<Integer> iterator() { 154 return new Iterator<Integer>() { 155 private int i = start; 156 157 public boolean hasNext() { 158 return i <= end; 159 } 160 161 public Integer next() { 162 return i++; 163 } 164 165 public void remove() { 166 throw new UnsupportedOperationException(); 167 } 168 }; 169 } 170 171 /*** 172 * Checks if given index is an endpoint of this interval (i.e. it is minimal 173 * or maximal index still belonging to this interval). 174 * 175 * @param i 176 * the index to check 177 * @return <code>true</code> if given index is an endpoint, 178 * <code>false</code> otherwise. 179 */ 180 public boolean isEndpoint(final int i) { 181 return (i == start) || (i == end); 182 } 183 184 /*** 185 * Compares this interval to the interval specified by two integers. 186 * 187 * @param start 188 * the first end of the interval 189 * @param end 190 * the second end of the interval 191 * @return <code>true</code> if two intervals contain the same items, 192 * <code>false</code> otherwise. 193 */ 194 public boolean equals(int start, int end) { 195 if (start < end) { 196 return (this.start == start) && (this.end == end); 197 } 198 return (this.end == start) && (this.start == end); 199 } 200 201 /*** 202 * Checks if given interval covers items at the beginning of this interval. 203 * 204 * @param other 205 * the other interval 206 * @return <code>true</code> if other interval covers items at the 207 * beginning of this interval. 208 */ 209 public boolean startsWith(final Interval other) { 210 if (other.isEmpty()) 211 return false; 212 return (this.start == other.start) && (this.end >= other.end); 213 } 214 215 /*** 216 * Checks if given interval covers items at the end of this interval. 217 * 218 * @param other 219 * the other interval 220 * @return <code>true</code> if other interval covers items at the 221 * end of this interval. 222 */ 223 public boolean endsWith(final Interval other) { 224 if (other.isEmpty()) 225 return false; 226 return (this.end == other.end) && (this.start <= other.start); 227 } 228 229 /*** 230 * Subtracts given interval from this one and returns result. 231 * 232 * @param other 233 * the interval to subtract 234 * @return an Interval instance if result is a single interval, an array of 235 * intervals otherwise. 236 */ 237 public Object subtract(final Interval other) { 238 if (start < other.start) { 239 if (end < other.start) { 240 return this; 241 } 242 if (end <= other.end) { 243 return Interval.fromRange(start, other.start - 1); 244 } 245 return new Interval[] { Interval.fromRange(start, other.start - 1), 246 Interval.fromRange(other.end + 1, end) }; 247 } else if (start == other.start) { 248 if (end <= other.end) { 249 return Interval.EMPTY; 250 } 251 return Interval.fromRange(other.end + 1, end); 252 } else { 253 if (start > other.end) { 254 return this; 255 } 256 if (end > other.end) { 257 return Interval.fromRange(other.end + 1, end); 258 } 259 return Interval.EMPTY; 260 } 261 } 262 }