001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package org.apache.geronimo.javamail.store.nntp.newsrc; 021 022 import java.io.IOException; 023 import java.io.Writer; 024 025 /** 026 * Represent a single Range in a newsrc file. A Range can be either a single 027 * number (start == end) or a span of article numbers. 028 */ 029 public class Range { 030 // the low end of the range 031 int start; 032 033 // the high end of the range (start and end are inclusive); 034 int end; 035 036 /** 037 * Construct a Range item for a single digit range. 038 * 039 * @param spot 040 * The location of the singleton. 041 */ 042 public Range(int spot) { 043 this(spot, spot); 044 } 045 046 /** 047 * Construct a Range item. 048 * 049 * @param start 050 * The starting point of the Range. 051 * @param end 052 * The Range end point (which may be equal to the starting 053 * point). 054 */ 055 public Range(int start, int end) { 056 this.start = start; 057 this.end = end; 058 } 059 060 /** 061 * Parse a section of a .newsrc range string into a single Range item. The 062 * range is either a single number, or a pair of numbers separated by a 063 * hyphen. 064 * 065 * @param range 066 * The range string. 067 * 068 * @return A constructed Range item, or null if there is a parsing error. 069 */ 070 static public Range parse(String range) { 071 // a range from a newsrc file is either a single number or in the format 072 // 'nnnn-mmmm'. We need 073 // to figure out which type this is. 074 int marker = range.indexOf('-'); 075 076 try { 077 if (marker != -1) { 078 String rangeStart = range.substring(0, marker).trim(); 079 String rangeEnd = range.substring(marker + 1).trim(); 080 081 int start = Integer.parseInt(rangeStart); 082 int end = Integer.parseInt(rangeEnd); 083 084 if (start >= 0 && end >= 0) { 085 return new Range(start, end); 086 } 087 } else { 088 // use the entire token 089 int start = Integer.parseInt(range); 090 // and start and the end are the same 091 return new Range(start, start); 092 093 } 094 } catch (NumberFormatException e) { 095 } 096 // return null for any bad values 097 return null; 098 } 099 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 }