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 }