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 }