View Javadoc

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 }