1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.geronimo.javamail.store.nntp.newsrc;
21
22 import java.io.IOException;
23 import java.io.Writer;
24
25 /**
26 * Represent a single Range in a newsrc file. A Range can be either a single
27 * number (start == end) or a span of article numbers.
28 */
29 public class Range {
30 // the low end of the range
31 int start;
32
33 // the high end of the range (start and end are inclusive);
34 int end;
35
36 /**
37 * Construct a Range item for a single digit range.
38 *
39 * @param spot
40 * The location of the singleton.
41 */
42 public Range(int spot) {
43 this(spot, spot);
44 }
45
46 /**
47 * Construct a Range item.
48 *
49 * @param start
50 * The starting point of the Range.
51 * @param end
52 * The Range end point (which may be equal to the starting
53 * point).
54 */
55 public Range(int start, int end) {
56 this.start = start;
57 this.end = end;
58 }
59
60 /**
61 * Parse a section of a .newsrc range string into a single Range item. The
62 * range is either a single number, or a pair of numbers separated by a
63 * hyphen.
64 *
65 * @param range
66 * The range string.
67 *
68 * @return A constructed Range item, or null if there is a parsing error.
69 */
70 static public Range parse(String range) {
71 // a range from a newsrc file is either a single number or in the format
72 // 'nnnn-mmmm'. We need
73 // to figure out which type this is.
74 int marker = range.indexOf('-');
75
76 try {
77 if (marker != -1) {
78 String rangeStart = range.substring(0, marker).trim();
79 String rangeEnd = range.substring(marker + 1).trim();
80
81 int start = Integer.parseInt(rangeStart);
82 int end = Integer.parseInt(rangeEnd);
83
84 if (start >= 0 && end >= 0) {
85 return new Range(start, end);
86 }
87 } else {
88 // use the entire token
89 int start = Integer.parseInt(range);
90 // and start and the end are the same
91 return new Range(start, start);
92
93 }
94 } catch (NumberFormatException e) {
95 }
96 // return null for any bad values
97 return null;
98 }
99
100 /**
101 * Get the starting point for the Range.
102 *
103 * @return The beginning of the mark range.
104 */
105 public int getStart() {
106 return start;
107 }
108
109 /**
110 * Set the starting point for a Range.
111 *
112 * @param start
113 * The new start value.
114 */
115 public void setStart(int start) {
116 this.start = start;
117 }
118
119 /**
120 * Get the ending point for the Range.
121 *
122 * @return The end of the mark range.
123 */
124 public int getEnd() {
125 return end;
126 }
127
128 /**
129 * Set the ending point for a Range.
130 *
131 * @param end
132 * The new end value.
133 */
134 public void setEnd(int end) {
135 this.end = end;
136 }
137
138 /**
139 * Test if a range contains a point value.
140 *
141 * @param target
142 * The article location to test.
143 *
144 * @return True if the target is between the start and end values,
145 * inclusive.
146 */
147 public boolean contains(int target) {
148 return target >= start && target <= end;
149 }
150
151 /**
152 * Test if one range is completely contained within another Range.
153 *
154 * @param other
155 * The other test range.
156 *
157 * @return true if the other start and end points are contained within this
158 * range.
159 */
160 public boolean contains(Range other) {
161 return contains(other.getStart()) && contains(other.getEnd());
162 }
163
164 /**
165 * Tests if two ranges overlap
166 *
167 * @param other
168 * The other test range.
169 *
170 * @return true if the start or end points of either range are contained
171 * within the range of the other.
172 */
173 public boolean overlaps(Range other) {
174 return other.contains(start) || other.contains(end) || contains(other.getStart()) || contains(other.getEnd());
175 }
176
177 /**
178 * Test if two ranges exactly abutt each other.
179 *
180 * @param other
181 * The other Range to test.
182 *
183 * @return true if the end of one range abutts the start of the other range.
184 */
185 public boolean abutts(Range other) {
186 return other.getStart() == end + 1 || other.getEnd() == start - 1;
187 }
188
189 /**
190 * Tests if a single point abutts either the start or end of this Range.
191 *
192 * @param article
193 * The point to test.
194 *
195 * @return true if test point is equal to start - 1 or end + 1.
196 */
197 public boolean abutts(int article) {
198 return article == start - 1 || article == end + 1;
199 }
200
201 /**
202 * Test if a point is below the test Range.
203 *
204 * @param article
205 * The point to test.
206 *
207 * @return true if the entire range is less than the test point.
208 */
209 public boolean lessThan(int article) {
210 return end < article;
211 }
212
213 /**
214 * Test if another Range is less than this Range.
215 *
216 * @param other
217 * The other Range to test.
218 *
219 * @return true if the other Range lies completely below this Range.
220 */
221 public boolean lessThan(Range other) {
222 return end < other.start;
223 }
224
225 /**
226 * Test if a point is above the test Range.
227 *
228 * @param article
229 * The point to test.
230 *
231 * @return true if the entire range is greater than the test point.
232 */
233 public boolean greaterThan(int article) {
234 return start > article;
235 }
236
237 /**
238 * Test if another Range is greater than this Range.
239 *
240 * @param other
241 * The other Range to test.
242 *
243 * @return true if the other Range lies completely below this Range.
244 */
245 public boolean greaterThan(Range other) {
246 return start > other.end;
247 }
248
249 /**
250 * Merge another Range into this one. Merging will increase the bounds of
251 * this Range to encompass the entire span of the two. If the Ranges do not
252 * overlap, the newly created range will include the gap between the two.
253 *
254 * @param other
255 * The Range to merge.
256 */
257 public void merge(Range other) {
258 if (other.start < start) {
259 start = other.start;
260 }
261
262 if (other.end > end) {
263 end = other.end;
264 }
265 }
266
267 /**
268 * Split a range at a given split point. Splitting will truncate at the
269 * split location - 1 and return a new range beginning at location + 1; This
270 * code assumes that the split location is at neither end poing.
271 *
272 * @param location
273 * The split location. Location must be in the range start <
274 * location < end.
275 *
276 * @return A new Range object for the split portion of the range.
277 */
278 public Range split(int location) {
279 int newEnd = end;
280
281 end = location - 1;
282
283 return new Range(location + 1, newEnd);
284 }
285
286 /**
287 * Save an individual range element to a newsrc file. The range is expressed
288 * either as a single number, or a hypenated pair of numbers.
289 *
290 * @param out
291 * The output writer used to save the data.
292 *
293 * @exception IOException
294 */
295 public void save(Writer out) throws IOException {
296 // do we have a single data point range?
297 if (start == end) {
298 out.write(Integer.toString(start));
299 } else {
300 out.write(Integer.toString(start));
301 out.write("-");
302 out.write(Integer.toString(end));
303 }
304 }
305
306 /**
307 * Convert a Range into String form. Used mostly for debugging.
308 *
309 * @return The String representation of the Range.
310 */
311 public String toString() {
312 if (start == end) {
313 return Integer.toString(start);
314 } else {
315 return Integer.toString(start) + "-" + Integer.toString(end);
316 }
317 }
318 }