View Javadoc

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