/******************************************************************************* * Copyright 2012 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.worldwind.common.util; /** * Represents an immutable range of comparable values (eg. <code>[minValue, maxValue]</code>) * * @author James Navin (james.navin@ga.gov.au) */ public class Range<C extends Comparable<C>> { private C minValue; private boolean includeMin = true; private C maxValue; private boolean includeMax = true; /** * Create a inclusive range <code>[minValue, maxValue]</code> */ public Range(C minValue, C maxValue) { this.minValue = minValue == null ? null : min(minValue, maxValue); this.maxValue = maxValue == null ? null : max(minValue, maxValue); } private C min(C value1, C value2) { if (value1 == null) { return value2; } if (value2 == null) { return value1; } if (value1.compareTo(value2) <= 0) { return value1; } return value2; } private C max(C value1, C value2) { if (value1 == null) { return value2; } if (value2 == null) { return value1; } if (value1.compareTo(value2) > 0) { return value1; } return value2; } /** * Create new range, specifying the inclusivity. */ public Range(C minValue, boolean includeMin, C maxValue, boolean includeMax) { this(minValue, maxValue); this.includeMin = includeMin; this.includeMax = includeMax; } /** * @return Whether this range is open on the left (lower) side */ public boolean isOpenLeft() { return minValue == null; } /** * @return Whether this range is inclusive of the left (minimum) value */ public boolean isInclusiveLeft() { return !isOpenLeft() && includeMin; } /** * @return Whether this range is open on the right (higher) side */ public boolean isOpenRight() { return maxValue == null; } /** * @return Whether this range is inclusive of the right (maximum) value */ public boolean isInclusiveRight() { return !isOpenRight() && includeMax; } public C getMinValue() { return minValue; } public C getMaxValue() { return maxValue; } /** * @return Whether the provided value is contained in this range */ public boolean contains(C value) { if (value == null) { return false; } return greaterThanMin(value) && lessThanMax(value); } private boolean lessThanMax(C value) { if (isOpenRight()) { return true; } int compareValue = maxValue.compareTo(value); if (isInclusiveRight()) { return compareValue >= 0; } return compareValue > 0; } private boolean greaterThanMin(C value) { if (isOpenLeft()) { return true; } int compareValue = minValue.compareTo(value); if (isInclusiveLeft()) { return compareValue <= 0; } return compareValue < 0; } @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { if (obj == this) { return true; } Range<C> other = null; try { other = (Range<C>)obj; } catch (Exception e) { return false; } return this.includeMax == other.includeMax && this.includeMin == other.includeMin && nullSafeEquals(minValue, other.minValue) && nullSafeEquals(maxValue, other.maxValue); } private boolean nullSafeEquals(Object o1, Object o2) { if (o1 == null && o2 == null) { return true; } if (o1 != null && o2 != null) { return o1.equals(o2); } return false; } @Override public int hashCode() { return (minValue == null ? minValue.hashCode() : 31) + (maxValue == null ? maxValue.hashCode() : 131); } /** * @return A new range that is the union of this range and the provided other */ public Range<C> union(Range<C> other) { C min = this.minValue == null || other.minValue == null ? null : min(this.minValue, other.minValue); boolean includeMin = min != null && ((min == this.minValue && this.includeMin) || (min == other.minValue && other.includeMin)); C max = this.maxValue == null || other.maxValue == null ? null : max(this.maxValue, other.maxValue); boolean includeMax = max != null && ((max == this.maxValue && this.includeMax) || (max == other.maxValue && other.includeMax)); return new Range<C>(min, includeMin, max, includeMax); } }