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