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 }