/*
*
* This file is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* For a copy of the GNU Lesser General Public License
* see .
*/
package eu.headcrashing.java.util;
import static java.lang.Integer.MAX_VALUE;
import static java.lang.Integer.MIN_VALUE;
/**
* A range is defined by a begin and an end. It allows checking whether a value
* is within the range or outside. A range can be open at one or both ends, i.
* e. the range is assumed to be endless in that direction.
*
* @author Markus KARG (markus@headcrashing.eu)
* @version 1.2.1
*/
public final class Range> {
private final T lowerBound;
private final T upperBound;
/**
* Creates a range with the specified bounds. The bounds will be included,
* i. e. are part of the range. Bounds can be declared as being open, i. e.
* the range is assumed to be endless in that direction.
*
* @param lowerBound
* The lowest possible value of the range, or {@code null} if
* there is no lower bound.
* @param upperBound
* The greatest possible value of the range, or {@code null} if
* there is no upper bound.
* @throws IllegalArgumentException
* if lower bound is greater than upper bound
*/
public Range(final T lowerBound, final T upperBound) throws IllegalArgumentException {
if (lowerBound != null && upperBound != null && lowerBound.compareTo(upperBound) > 0)
throw new IllegalArgumentException("lowerBound is greater than upperBound");
this.lowerBound = lowerBound;
this.upperBound = upperBound;
}
/**
* Checks whether the specified object is within the range, including
* bounds.
*
* @param object
* The object to be checked. Must not be {@code null}.
* @return {@code false} if {@code object} is lower than the lower bound or
* greater than the upper bound; otherwise {@code true}.
* @throws IllegalArgumentException
* if {@code object} is {@code null}.
*/
public final boolean contains(final T object) throws IllegalArgumentException {
if (object == null)
throw new IllegalArgumentException("object is null");
if (this.lowerBound != null && object.compareTo(this.lowerBound) < 0)
return false;
if (this.upperBound != null && object.compareTo(this.upperBound) > 0)
return false;
return true;
}
/**
* Checks whether the specified range is entirely contained in the range,
* including bounds.
*
* @param range
* The range to be checked. Must not be {@code null}.
* @return {@code false} if {@code range} has a lower bound lower than the
* lower bound of this or an upper bound greater than the upper
* bound of this (i. e. {@code other} overlaps or is completely
* outside); otherwise {@code true}.
* @throws IllegalArgumentException
* if {@code other} is {@code null}.
* @since 1.1.0
*/
public final boolean contains(final Range range) throws IllegalArgumentException {
if (range == null)
throw new IllegalArgumentException("range is null");
if (this.lowerBound != null && (range.lowerBound == null || range.lowerBound.compareTo(this.lowerBound) < 0))
return false;
if (this.upperBound != null && (range.upperBound == null || range.upperBound.compareTo(this.upperBound) > 0))
return false;
return true;
}
/**
* Checks whether the specified range overlaps this range (i. e. whether the
* ranges intersect).
*
* @param range
* The {@code range} to be checked. Must not be {@code null}.
* @return {@code false} if {@code range} has an upper bound lower than the
* lower bound of this or a lower bound greater than the upper bound
* of this; otherwise {@code true}.
* @throws IllegalArgumentException
* if {@code range} is {@code null}.
* @since 1.2.0
*/
public final boolean overlaps(final Range range) throws IllegalArgumentException {
if (range == null)
throw new IllegalArgumentException("range is null");
if (this.upperBound != null && range.lowerBound != null && this.upperBound.compareTo(range.lowerBound) < 0)
return false;
if (this.lowerBound != null && range.upperBound != null && this.lowerBound.compareTo(range.upperBound) > 0)
return false;
return true;
}
@Override
public final int hashCode() {
// MAX_value is used for open LOWER bound as it will be most far away
// from any existing LOWER bound.
final int lowerBoundHashCode = this.lowerBound == null ? MAX_VALUE : this.lowerBound.hashCode();
// MIN_value is used for open UPPER bound as it will be most far away
// from any existing UPPER bound.
final int upperBoundHashCode = this.upperBound == null ? MIN_VALUE : this.upperBound.hashCode();
return lowerBoundHashCode << 16 ^ upperBoundHashCode;
}
@Override
public final boolean equals(final Object other) {
if (!(other instanceof Range))
return false;
final Range> that = (Range>) other;
return equals(this.lowerBound, that.lowerBound) && equals(this.upperBound, that.upperBound);
}
private static final boolean equals(final Object a, final Object b) {
if (a == null && b == null)
return true;
if (a != null && b != null)
return a.equals(b);
return false;
}
@Override
public final String toString() {
return String.format("Range[%s, %s]", this.lowerBound, this.upperBound);
}
}