001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.lang3; 018 019 import java.io.Serializable; 020 import java.util.Comparator; 021 022 /** 023 * <p><code>Range</code> represents an immutable range of numbers of the same type.</p> 024 * <p>The objects need to either be implementations of <code>java.lang.Comparable</code> 025 * or you need to supply a <code>java.util.Comparator</code>. </p> 026 * 027 * <p>#ThreadSafe#</p> 028 * @author Apache Software Foundation 029 * @since 3.0 030 * @version $Id: Range.java 967237 2010-07-23 20:08:57Z mbenson $ 031 */ 032 public final class Range<T> implements Serializable { 033 034 /** 035 * Required for serialization support. 036 * 037 * @see java.io.Serializable 038 */ 039 private static final long serialVersionUID = 1L; 040 041 /** 042 * The ordering scheme used in this range. 043 */ 044 private final Comparator<T> comparator; 045 046 /** 047 * The minimum value in this range (inclusive). 048 */ 049 private final T minimum; 050 051 /** 052 * The maximum value in this range (inclusive). 053 */ 054 private final T maximum; 055 056 /** 057 * Cached output hashCode (class is immutable). 058 */ 059 private transient int hashCode = 0; 060 061 /** 062 * Cached output toString (class is immutable). 063 */ 064 private transient String toString = null; 065 066 /** 067 * <p>Constructs a new <code>Range</code> using the specified 068 * element as both the minimum and maximum in this range.</p> 069 * <p>The range uses the natural ordering of the elements to 070 * determine where values lie in the range.</p> 071 * 072 * @param element the value to use for this range, must not be <code>null</code> 073 * @return the new range object 074 * @throws IllegalArgumentException if the value is <code>null</code> 075 * @throws ClassCastException if the value is not Comparable 076 */ 077 public static <T extends Comparable<T>> Range<T> is(T element) { 078 return new Range<T>(element, element, ComparableComparator.<T>getInstance()); 079 } 080 081 /** 082 * <p>Constructs a new <code>Range</code> with the specified 083 * minimum and maximum values (both inclusive).</p> 084 * <p>The range uses the natural ordering of the elements to 085 * determine where values lie in the range.</p> 086 * 087 * <p>The arguments may be passed in the order (min,max) or (max,min). The 088 * getMinimum and getMaximum methods will return the correct values.</p> 089 * 090 * @param element1 first value that defines the edge of the range, inclusive 091 * @param element2 second value that defines the edge of the range, inclusive 092 * @return the new range object 093 * @throws IllegalArgumentException if either value is <code>null</code> 094 * @throws ClassCastException if either value is not Comparable 095 */ 096 public static <T extends Comparable<T>> Range<T> between(T element1, T element2) { 097 return new Range<T>( element1, element2, ComparableComparator.<T>getInstance()); 098 } 099 100 /** 101 * <p>Constructs a new <code>Range</code> using the specified 102 * element as both the minimum and maximum in this range.</p> 103 * <p>The range uses the passed in <code>Comparator</code> to 104 * determine where values lie in the range.</p> 105 * 106 * @param element the value to use for this range, must not be <code>null</code> 107 * @param c comparator to be used 108 * @return the new range object 109 * @throws IllegalArgumentException if the value is <code>null</code> 110 */ 111 public static <T> Range<T> is(T element, Comparator<T> c) { 112 return new Range<T>(element, element, c); 113 } 114 115 /** 116 * <p>Constructs a new <code>Range</code> with the specified 117 * minimum and maximum values (both inclusive).</p> 118 * <p>The range uses the passed in <code>Comparator</code> to 119 * determine where values lie in the range.</p> 120 * 121 * <p>The arguments may be passed in the order (min,max) or (max,min). The 122 * getMinimum and getMaximum methods will return the correct values.</p> 123 * 124 * @param element1 first value that defines the edge of the range, inclusive 125 * @param element2 second value that defines the edge of the range, inclusive 126 * @param c comparator to be used 127 * @return the new range object 128 * @throws IllegalArgumentException if either value is <code>null</code> 129 */ 130 public static <T> Range<T> between(T element1, T element2, Comparator<T> c) { 131 return new Range<T>(element1, element2, c); 132 } 133 134 private Range(T element1, T element2, Comparator<T> c) { 135 if(element1 == null || element2 == null) { 136 throw new IllegalArgumentException("Elements in a range must not be null: element1=" + 137 element1 + ", element2=" + element2); 138 } 139 140 if(c == null) { 141 throw new IllegalArgumentException("Comparator must not be null"); 142 } 143 144 if(c.compare(element1, element2) < 1) { 145 this.minimum = element1; 146 this.maximum = element2; 147 } else { 148 this.minimum = element2; 149 this.maximum = element1; 150 } 151 this.comparator = c; 152 } 153 154 // Accessors 155 //-------------------------------------------------------------------- 156 157 /** 158 * <p>Gets the minimum value in this range.</p> 159 * 160 * @return the minimum value in this range 161 */ 162 public T getMinimum() { 163 return this.minimum; 164 } 165 166 /** 167 * <p>Gets the maximum value in this range.</p> 168 * 169 * @return the maximum value in this range 170 */ 171 public T getMaximum() { 172 return this.maximum; 173 } 174 175 /** 176 * <p>Gets the comparator being used to determine if objects are within the range. </p> 177 * 178 * @return the comparator being used 179 */ 180 public Comparator<T> getComparator() { 181 return this.comparator; 182 } 183 184 /** 185 * <p>Whether or not the Range is using the default natural comparison method 186 * to compare elements. </p> 187 * 188 * @return whether or not the default Comparator is in use 189 */ 190 public boolean isDefaultNaturalOrdering() { 191 return this.comparator == ComparableComparator.INSTANCE; 192 } 193 194 // Include tests 195 //-------------------------------------------------------------------- 196 197 /** 198 * <p>Tests whether the specified element occurs within this range.</p> 199 * 200 * <p><code>null</code> is handled and returns <code>false</code>.</p> 201 * 202 * @param element the element to test, may be <code>null</code> 203 * @return <code>true</code> if the specified element occurs within this range 204 */ 205 public boolean contains(T element) { 206 if(element == null) { 207 return false; 208 } 209 return (comparator.compare(element, this.minimum) > -1) && (comparator.compare(element, this.maximum) < 1); 210 } 211 212 /** 213 * <p>Tests whether the specified element occurs before this range.</p> 214 * 215 * <p><code>null</code> is handled and returns <code>false</code>.</p> 216 * 217 * @param element the element to test, may be <code>null</code> 218 * @return <code>true</code> if the specified element occurs before this range 219 */ 220 public boolean elementBefore(T element) { 221 if (element == null) { 222 return false; 223 } 224 225 return this.comparator.compare(element, this.minimum) < 0; 226 } 227 228 /** 229 * <p>Tests whether the specified element occurs after this range.</p> 230 * 231 * <p><code>null</code> is handled and returns <code>false</code>.</p> 232 * 233 * @param element the element to test, may be <code>null</code> 234 * @return <code>true</code> if the specified element occurs after this range 235 */ 236 public boolean elementAfter(T element) { 237 if (element == null) { 238 return false; 239 } 240 241 return this.comparator.compare(element, this.maximum) > 0; 242 } 243 244 /** 245 * <p>Tests where the specified element occurs relative to this range.</p> 246 * <p>The API is reminiscent of the Comparable interface returning <code>-1</code> if 247 * the element is before the range, <code>0</code> if contained within the range and 248 * <code>1</code> if the element is after the range. </p> 249 * 250 * @param element the element to test 251 * @return -1, 0 or +1 depending on the element's location relative to the range 252 */ 253 public int elementCompareTo(T element) { 254 if(element == null) { 255 // Comparable API says throw NPE on null 256 throw new NullPointerException("Element is null"); 257 } 258 if(elementBefore(element)) { 259 return -1; 260 } else 261 if(elementAfter(element)) { 262 return 1; 263 } else { 264 return 0; 265 } 266 } 267 268 // Range tests 269 //-------------------------------------------------------------------- 270 271 /** 272 * <p>Tests whether the specified range occurs entirely within this range.</p> 273 * 274 * <p><code>null</code> is handled and returns <code>false</code>.</p> 275 * 276 * @param range the range to test, may be <code>null</code> 277 * @return <code>true</code> if the specified range occurs entirely within 278 * this range; otherwise, <code>false</code> 279 * @throws IllegalArgumentException if the <code>Range</code> cannot be compared 280 */ 281 public boolean containsRange(Range<T> range) { 282 if (range == null) { 283 return false; 284 } 285 return contains(range.getMinimum()) 286 && contains(range.getMaximum()); 287 } 288 289 /** 290 * <p>Tests whether the specified range overlaps with this range.</p> 291 * 292 * <p><code>null</code> is handled and returns <code>false</code>.</p> 293 * 294 * @param range the range to test, may be <code>null</code> 295 * @return <code>true</code> if the specified range overlaps with this 296 * range; otherwise, <code>false</code> 297 * @throws IllegalArgumentException if the <code>Range</code> cannot be compared 298 */ 299 public boolean overlapsRange(Range<T> range) { 300 if (range == null) { 301 return false; 302 } 303 return range.contains(this.minimum) 304 || range.contains(this.maximum) 305 || contains(range.getMinimum()); 306 } 307 308 // Basics 309 //-------------------------------------------------------------------- 310 311 /** 312 * <p>Compares this range to another object to test if they are equal.</p>. 313 * 314 * <p>To be equal, the class, minimum and maximum must be equal.</p> 315 * 316 * @param obj the reference object with which to compare 317 * @return <code>true</code> if this object is equal 318 */ 319 @Override 320 public boolean equals(Object obj) { 321 if (obj == this) { 322 return true; 323 } else if (obj == null || obj.getClass() != getClass()) { 324 return false; 325 } else { 326 @SuppressWarnings("unchecked") // OK because we checked the class above 327 Range<T> range = (Range<T>) obj; 328 return getMinimum().equals(range.getMinimum()) && 329 getMaximum().equals(range.getMaximum()); 330 } 331 } 332 333 /** 334 * <p>Gets a hashCode for the range.</p> 335 * 336 * @return a hash code value for this object 337 */ 338 @Override 339 public int hashCode() { 340 int result = hashCode; 341 if (hashCode == 0) { 342 result = 17; 343 result = 37 * result + getClass().hashCode(); 344 result = 37 * result + this.minimum.hashCode(); 345 result = 37 * result + this.maximum.hashCode(); 346 hashCode = result; 347 } 348 return result; 349 } 350 351 /** 352 * <p>Gets the range as a <code>String</code>.</p> 353 * 354 * <p>The format of the String is 'Range[<i>min</i>,<i>max</i>]'.</p> 355 * 356 * @return the <code>String</code> representation of this range 357 */ 358 @Override 359 public String toString() { 360 String result = toString; 361 if (result == null) { 362 StringBuilder buf = new StringBuilder(32); 363 buf.append("Range["); 364 buf.append(this.minimum); 365 buf.append(','); 366 buf.append(this.maximum); 367 buf.append(']'); 368 result = buf.toString(); 369 toString = result; 370 } 371 return result; 372 } 373 374 // Taken from Commons Collections - documentation removed as not a public class 375 private static class ComparableComparator<E extends Comparable<? super E>> implements Comparator<E>, Serializable { 376 377 private static final long serialVersionUID = 1L; 378 379 @SuppressWarnings("rawtypes") // Comparator works for all types 380 public static final ComparableComparator<?> INSTANCE = new ComparableComparator(); 381 382 @SuppressWarnings("unchecked") // OK to cast, because comparator works for all types 383 public static <E extends Comparable<? super E>> ComparableComparator<E> getInstance() { 384 return (ComparableComparator<E>) INSTANCE; 385 } 386 387 public ComparableComparator() { 388 super(); 389 } 390 391 public int compare(E obj1, E obj2) { 392 return obj1.compareTo(obj2); 393 } 394 395 @Override 396 public int hashCode() { 397 return "ComparableComparator".hashCode(); 398 } 399 400 @Override 401 public boolean equals(Object object) { 402 return (this == object) || 403 ((null != object) && (object.getClass().equals(this.getClass()))); 404 } 405 406 } 407 408 }