Coverage Report - org.apache.commons.lang3.Range
 
Classes in this File Line Coverage Branch Coverage Complexity
Range
98%
86/87
92%
72/78
2,88
Range$ComparableComparator
100%
4/4
N/A
2,88
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  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  
 package org.apache.commons.lang3;
 18  
 
 19  
 import java.io.Serializable;
 20  
 import java.util.Comparator;
 21  
 
 22  
 /**
 23  
  * <p>An immutable range of objects from a minimum to maximum point inclusive.</p>
 24  
  * 
 25  
  * <p>The objects need to either be implementations of {@code Comparable}
 26  
  * or you need to supply a {@code Comparator}. </p>
 27  
  *
 28  
  * <p>#ThreadSafe# if the objects and comparator are thread-safe</p>
 29  
  * 
 30  
  * @since 3.0
 31  
  * @version $Id: Range.java 1565243 2014-02-06 13:37:12Z sebb $
 32  
  */
 33  
 public final class Range<T> implements Serializable {
 34  
 
 35  
     /**
 36  
      * Serialization version.
 37  
      * @see java.io.Serializable
 38  
      */
 39  
     private static final long serialVersionUID = 1L;
 40  
 
 41  
     /**
 42  
      * The ordering scheme used in this range.
 43  
      */
 44  
     private final Comparator<T> comparator;
 45  
     /**
 46  
      * The minimum value in this range (inclusive).
 47  
      */
 48  
     private final T minimum;
 49  
     /**
 50  
      * The maximum value in this range (inclusive).
 51  
      */
 52  
     private final T maximum;
 53  
     /**
 54  
      * Cached output hashCode (class is immutable).
 55  
      */
 56  
     private transient int hashCode;
 57  
     /**
 58  
      * Cached output toString (class is immutable).
 59  
      */
 60  
     private transient String toString;
 61  
 
 62  
     /**
 63  
      * <p>Obtains a range using the specified element as both the minimum
 64  
      * and maximum in this range.</p>
 65  
      * 
 66  
      * <p>The range uses the natural ordering of the elements to determine where
 67  
      * values lie in the range.</p>
 68  
      *
 69  
      * @param <T> the type of the elements in this range
 70  
      * @param element  the value to use for this range, not null
 71  
      * @return the range object, not null
 72  
      * @throws IllegalArgumentException if the element is null
 73  
      * @throws ClassCastException if the element is not {@code Comparable}
 74  
      */
 75  
     public static <T extends Comparable<T>> Range<T> is(final T element) {
 76  2
         return between(element, element, null);
 77  
     }
 78  
 
 79  
     /**
 80  
      * <p>Obtains a range using the specified element as both the minimum
 81  
      * and maximum in this range.</p>
 82  
      * 
 83  
      * <p>The range uses the specified {@code Comparator} to determine where
 84  
      * values lie in the range.</p>
 85  
      *
 86  
      * @param <T> the type of the elements in this range
 87  
      * @param element  the value to use for this range, must not be {@code null}
 88  
      * @param comparator  the comparator to be used, null for natural ordering
 89  
      * @return the range object, not null
 90  
      * @throws IllegalArgumentException if the element is null
 91  
      * @throws ClassCastException if using natural ordering and the elements are not {@code Comparable}
 92  
      */
 93  
     public static <T> Range<T> is(final T element, final Comparator<T> comparator) {
 94  1
         return between(element, element, comparator);
 95  
     }
 96  
 
 97  
     /**
 98  
      * <p>Obtains a range with the specified minimum and maximum values (both inclusive).</p>
 99  
      * 
 100  
      * <p>The range uses the natural ordering of the elements to determine where
 101  
      * values lie in the range.</p>
 102  
      *
 103  
      * <p>The arguments may be passed in the order (min,max) or (max,min).
 104  
      * The getMinimum and getMaximum methods will return the correct values.</p>
 105  
      *
 106  
      * @param <T> the type of the elements in this range
 107  
      * @param fromInclusive  the first value that defines the edge of the range, inclusive
 108  
      * @param toInclusive  the second value that defines the edge of the range, inclusive
 109  
      * @return the range object, not null
 110  
      * @throws IllegalArgumentException if either element is null
 111  
      * @throws ClassCastException if the elements are not {@code Comparable}
 112  
      */
 113  
     public static <T extends Comparable<T>> Range<T> between(final T fromInclusive, final T toInclusive) {
 114  211
         return between(fromInclusive, toInclusive, null);
 115  
     }
 116  
 
 117  
     /**
 118  
      * <p>Obtains a range with the specified minimum and maximum values (both inclusive).</p>
 119  
      * 
 120  
      * <p>The range uses the specified {@code Comparator} to determine where
 121  
      * values lie in the range.</p>
 122  
      *
 123  
      * <p>The arguments may be passed in the order (min,max) or (max,min).
 124  
      * The getMinimum and getMaximum methods will return the correct values.</p>
 125  
      *
 126  
      * @param <T> the type of the elements in this range
 127  
      * @param fromInclusive  the first value that defines the edge of the range, inclusive
 128  
      * @param toInclusive  the second value that defines the edge of the range, inclusive
 129  
      * @param comparator  the comparator to be used, null for natural ordering
 130  
      * @return the range object, not null
 131  
      * @throws IllegalArgumentException if either element is null
 132  
      * @throws ClassCastException if using natural ordering and the elements are not {@code Comparable}
 133  
      */
 134  
     public static <T> Range<T> between(final T fromInclusive, final T toInclusive, final Comparator<T> comparator) {
 135  216
         return new Range<T>(fromInclusive, toInclusive, comparator);
 136  
     }
 137  
 
 138  
     /**
 139  
      * Creates an instance.
 140  
      *
 141  
      * @param element1  the first element, not null
 142  
      * @param element2  the second element, not null
 143  
      * @param comp  the comparator to be used, null for natural ordering
 144  
      */
 145  
     @SuppressWarnings("unchecked")
 146  216
     private Range(final T element1, final T element2, final Comparator<T> comp) {
 147  216
         if (element1 == null || element2 == null) {
 148  0
             throw new IllegalArgumentException("Elements in a range must not be null: element1=" +
 149  
                                                element1 + ", element2=" + element2);
 150  
         }
 151  216
         if (comp == null) {
 152  213
             this.comparator = ComparableComparator.INSTANCE;
 153  
         } else {
 154  3
             this.comparator = comp;            
 155  
         }
 156  216
         if (this.comparator.compare(element1, element2) < 1) {
 157  212
             this.minimum = element1;
 158  212
             this.maximum = element2;
 159  
         } else {
 160  4
             this.minimum = element2;
 161  4
             this.maximum = element1;
 162  
         }
 163  216
     }
 164  
 
 165  
     // Accessors
 166  
     //--------------------------------------------------------------------
 167  
 
 168  
     /**
 169  
      * <p>Gets the minimum value in this range.</p>
 170  
      *
 171  
      * @return the minimum value in this range, not null
 172  
      */
 173  
     public T getMinimum() {
 174  4
         return minimum;
 175  
     }
 176  
 
 177  
     /**
 178  
      * <p>Gets the maximum value in this range.</p>
 179  
      *
 180  
      * @return the maximum value in this range, not null
 181  
      */
 182  
     public T getMaximum() {
 183  4
         return maximum;
 184  
     }
 185  
 
 186  
     /**
 187  
      * <p>Gets the comparator being used to determine if objects are within the range.</p>
 188  
      * 
 189  
      * <p>Natural ordering uses an internal comparator implementation, thus this
 190  
      * method never returns null. See {@link #isNaturalOrdering()}.</p>
 191  
      *
 192  
      * @return the comparator being used, not null
 193  
      */
 194  
     public Comparator<T> getComparator() {
 195  3
         return comparator;
 196  
     }
 197  
 
 198  
     /**
 199  
      * <p>Whether or not the Range is using the natural ordering of the elements.</p>
 200  
      * 
 201  
      * <p>Natural ordering uses an internal comparator implementation, thus this
 202  
      * method is the only way to check if a null comparator was specified.</p>
 203  
      *
 204  
      * @return true if using natural ordering
 205  
      */
 206  
     public boolean isNaturalOrdering() {
 207  2
         return comparator == ComparableComparator.INSTANCE;
 208  
     }
 209  
 
 210  
     // Element tests
 211  
     //--------------------------------------------------------------------
 212  
 
 213  
     /**
 214  
      * <p>Checks whether the specified element occurs within this range.</p>
 215  
      *
 216  
      * @param element  the element to check for, null returns false
 217  
      * @return true if the specified element occurs within this range
 218  
      */
 219  
     public boolean contains(final T element) {
 220  71
         if (element == null) {
 221  5
             return false;
 222  
         }
 223  66
         return comparator.compare(element, minimum) > -1 && comparator.compare(element, maximum) < 1;
 224  
     }
 225  
 
 226  
     /**
 227  
      * <p>Checks whether this range is after the specified element.</p>
 228  
      *
 229  
      * @param element  the element to check for, null returns false
 230  
      * @return true if this range is entirely after the specified element
 231  
      */
 232  
     public boolean isAfter(final T element) {
 233  18
         if (element == null) {
 234  1
             return false;
 235  
         }
 236  17
         return comparator.compare(element, minimum) < 0;
 237  
     }
 238  
 
 239  
     /**
 240  
      * <p>Checks whether this range starts with the specified element.</p>
 241  
      *
 242  
      * @param element  the element to check for, null returns false
 243  
      * @return true if the specified element occurs within this range
 244  
      */
 245  
     public boolean isStartedBy(final T element) {
 246  6
         if (element == null) {
 247  1
             return false;
 248  
         }
 249  5
         return comparator.compare(element, minimum) == 0;
 250  
     }
 251  
 
 252  
     /**
 253  
      * <p>Checks whether this range starts with the specified element.</p>
 254  
      *
 255  
      * @param element  the element to check for, null returns false
 256  
      * @return true if the specified element occurs within this range
 257  
      */
 258  
     public boolean isEndedBy(final T element) {
 259  6
         if (element == null) {
 260  1
             return false;
 261  
         }
 262  5
         return comparator.compare(element, maximum) == 0;
 263  
     }
 264  
 
 265  
     /**
 266  
      * <p>Checks whether this range is before the specified element.</p>
 267  
      *
 268  
      * @param element  the element to check for, null returns false
 269  
      * @return true if this range is entirely before the specified element
 270  
      */
 271  
     public boolean isBefore(final T element) {
 272  17
         if (element == null) {
 273  1
             return false;
 274  
         }
 275  16
         return comparator.compare(element, maximum) > 0;
 276  
     }
 277  
 
 278  
     /**
 279  
      * <p>Checks where the specified element occurs relative to this range.</p>
 280  
      * 
 281  
      * <p>The API is reminiscent of the Comparable interface returning {@code -1} if
 282  
      * the element is before the range, {@code 0} if contained within the range and
 283  
      * {@code 1} if the element is after the range. </p>
 284  
      *
 285  
      * @param element  the element to check for, not null
 286  
      * @return -1, 0 or +1 depending on the element's location relative to the range
 287  
      */
 288  
     public int elementCompareTo(final T element) {
 289  6
         if (element == null) {
 290  
             // Comparable API says throw NPE on null
 291  1
             throw new NullPointerException("Element is null");
 292  
         }
 293  5
         if (isAfter(element)) {
 294  1
             return -1;
 295  4
         } else if (isBefore(element)) {
 296  1
             return 1;
 297  
         } else {
 298  3
             return 0;
 299  
         }
 300  
     }
 301  
 
 302  
     // Range tests
 303  
     //--------------------------------------------------------------------
 304  
 
 305  
     /**
 306  
      * <p>Checks whether this range contains all the elements of the specified range.</p>
 307  
      *
 308  
      * <p>This method may fail if the ranges have two different comparators or element types.</p>
 309  
      *
 310  
      * @param otherRange  the range to check, null returns false
 311  
      * @return true if this range contains the specified range
 312  
      * @throws RuntimeException if ranges cannot be compared
 313  
      */
 314  
     public boolean containsRange(final Range<T> otherRange) {
 315  12
         if (otherRange == null) {
 316  1
             return false;
 317  
         }
 318  11
         return contains(otherRange.minimum)
 319  
             && contains(otherRange.maximum);
 320  
     }
 321  
 
 322  
     /**
 323  
      * <p>Checks whether this range is completely after the specified range.</p>
 324  
      *
 325  
      * <p>This method may fail if the ranges have two different comparators or element types.</p>
 326  
      *
 327  
      * @param otherRange  the range to check, null returns false
 328  
      * @return true if this range is completely after the specified range
 329  
      * @throws RuntimeException if ranges cannot be compared
 330  
      */
 331  
     public boolean isAfterRange(final Range<T> otherRange) {
 332  8
         if (otherRange == null) {
 333  1
             return false;
 334  
         }
 335  7
         return isAfter(otherRange.maximum);
 336  
     }
 337  
 
 338  
     /**
 339  
      * <p>Checks whether this range is overlapped by the specified range.</p>
 340  
      * 
 341  
      * <p>Two ranges overlap if there is at least one element in common.</p>
 342  
      *
 343  
      * <p>This method may fail if the ranges have two different comparators or element types.</p>
 344  
      *
 345  
      * @param otherRange  the range to test, null returns false
 346  
      * @return true if the specified range overlaps with this
 347  
      *  range; otherwise, {@code false}
 348  
      * @throws RuntimeException if ranges cannot be compared
 349  
      */
 350  
     public boolean isOverlappedBy(final Range<T> otherRange) {
 351  20
         if (otherRange == null) {
 352  2
             return false;
 353  
         }
 354  18
         return otherRange.contains(minimum)
 355  
             || otherRange.contains(maximum)
 356  
             || contains(otherRange.minimum);
 357  
     }
 358  
 
 359  
     /**
 360  
      * <p>Checks whether this range is completely before the specified range.</p>
 361  
      *
 362  
      * <p>This method may fail if the ranges have two different comparators or element types.</p>
 363  
      *
 364  
      * @param otherRange  the range to check, null returns false
 365  
      * @return true if this range is completely before the specified range
 366  
      * @throws RuntimeException if ranges cannot be compared
 367  
      */
 368  
     public boolean isBeforeRange(final Range<T> otherRange) {
 369  8
         if (otherRange == null) {
 370  1
             return false;
 371  
         }
 372  7
         return isBefore(otherRange.minimum);
 373  
     }
 374  
 
 375  
     /**
 376  
      * Calculate the intersection of {@code this} and an overlapping Range.
 377  
      * @param other overlapping Range
 378  
      * @return range representing the intersection of {@code this} and {@code other} ({@code this} if equal)
 379  
      * @throws IllegalArgumentException if {@code other} does not overlap {@code this}
 380  
      * @since 3.0.1
 381  
      */
 382  
     public Range<T> intersectionWith(final Range<T> other) {
 383  8
         if (!this.isOverlappedBy(other)) {
 384  2
             throw new IllegalArgumentException(String.format(
 385  
                 "Cannot calculate intersection with non-overlapping range %s", other));
 386  
         }
 387  6
         if (this.equals(other)) {
 388  5
             return this;
 389  
         }
 390  1
         final T min = getComparator().compare(minimum, other.minimum) < 0 ? other.minimum : minimum;
 391  1
         final T max = getComparator().compare(maximum, other.maximum) < 0 ? maximum : other.maximum;
 392  1
         return between(min, max, getComparator());
 393  
     }
 394  
 
 395  
     // Basics
 396  
     //--------------------------------------------------------------------
 397  
 
 398  
     /**
 399  
      * <p>Compares this range to another object to test if they are equal.</p>.
 400  
      *
 401  
      * <p>To be equal, the minimum and maximum values must be equal, which
 402  
      * ignores any differences in the comparator.</p>
 403  
      *
 404  
      * @param obj the reference object with which to compare
 405  
      * @return true if this object is equal
 406  
      */
 407  
     @Override
 408  
     public boolean equals(final Object obj) {
 409  16
         if (obj == this) {
 410  10
             return true;
 411  6
         } else if (obj == null || obj.getClass() != getClass()) {
 412  2
             return false;
 413  
         } else {
 414  
             @SuppressWarnings("unchecked") // OK because we checked the class above
 415  
             final
 416  4
             Range<T> range = (Range<T>) obj;
 417  4
             return minimum.equals(range.minimum) &&
 418  
                    maximum.equals(range.maximum);
 419  
         }
 420  
     }
 421  
 
 422  
     /**
 423  
      * <p>Gets a suitable hash code for the range.</p>
 424  
      *
 425  
      * @return a hash code value for this object
 426  
      */
 427  
     @Override
 428  
     public int hashCode() {
 429  7
         int result = hashCode;
 430  7
         if (hashCode == 0) {
 431  4
             result = 17;
 432  4
             result = 37 * result + getClass().hashCode();
 433  4
             result = 37 * result + minimum.hashCode();
 434  4
             result = 37 * result + maximum.hashCode();
 435  4
             hashCode = result;
 436  
         }
 437  7
         return result;
 438  
     }
 439  
 
 440  
     /**
 441  
      * <p>Gets the range as a {@code String}.</p>
 442  
      *
 443  
      * <p>The format of the String is '[<i>min</i>..<i>max</i>]'.</p>
 444  
      *
 445  
      * @return the {@code String} representation of this range
 446  
      */
 447  
     @Override
 448  
     public String toString() {
 449  4
         String result = toString;
 450  4
         if (result == null) {
 451  4
             final StringBuilder buf = new StringBuilder(32);
 452  4
             buf.append('[');
 453  4
             buf.append(minimum);
 454  4
             buf.append("..");
 455  4
             buf.append(maximum);
 456  4
             buf.append(']');
 457  4
             result = buf.toString();
 458  4
             toString = result;
 459  
         }
 460  4
         return result;
 461  
     }
 462  
 
 463  
     /**
 464  
      * <p>Formats the receiver using the given format.</p>
 465  
      * 
 466  
      * <p>This uses {@link java.util.Formattable} to perform the formatting. Three variables may
 467  
      * be used to embed the minimum, maximum and comparator.
 468  
      * Use {@code %1$s} for the minimum element, {@code %2$s} for the maximum element
 469  
      * and {@code %3$s} for the comparator.
 470  
      * The default format used by {@code toString()} is {@code [%1$s..%2$s]}.</p>
 471  
      * 
 472  
      * @param format  the format string, optionally containing {@code %1$s}, {@code %2$s} and  {@code %3$s}, not null
 473  
      * @return the formatted string, not null
 474  
      */
 475  
     public String toString(final String format) {
 476  1
         return String.format(format, minimum, maximum, comparator);
 477  
     }
 478  
 
 479  
     //-----------------------------------------------------------------------
 480  2
     @SuppressWarnings({"rawtypes", "unchecked"})
 481  1
     private enum ComparableComparator implements Comparator {
 482  1
         INSTANCE;
 483  
         /**
 484  
          * Comparable based compare implementation. 
 485  
          *
 486  
          * @param obj1 left hand side of comparison
 487  
          * @param obj2 right hand side of comparison
 488  
          * @return negative, 0, positive comparison value
 489  
          */
 490  
         @Override
 491  
         public int compare(final Object obj1, final Object obj2) {
 492  364
             return ((Comparable) obj1).compareTo(obj2);
 493  
         }
 494  
     }
 495  
 
 496  
 }