Java/Collections Data Structure/Range
Версия от 18:01, 31 мая 2010; (обсуждение)
Содержание
- 1 Byte Range
- 2 Finds the value in the range (start,limit) of the largest element (rank) where the count of all smaller elements in that range is less than or equals target.
- 3 IntRange represents an inclusive range of ints.
- 4 LongRange represents an inclusive range of longs.
- 5 NumberRange represents an inclusive range of java.lang.Number objects of the same type.
- 6 Represents a range of Number objects.
- 7 Represents a sequence of integer values, either ascending or descending.
- 8 This constructs an Iterator over each day in a date range defined by a focus date and range style.
Byte Range
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @version $Id: ByteRange.java 587751 2007-10-24 02:41:36Z vgritsenko $
*/
final public class ByteRange {
private final long start;
private final long end;
public ByteRange(long start, long end) {
this.start = start;
this.end = end;
}
public ByteRange(String string) throws NumberFormatException {
string = string.trim();
int dashPos = string.indexOf("-");
int length = string.length();
if (string.indexOf(",") != -1) {
throw new NumberFormatException("Simple ByteRange String contains a comma.");
}
if (dashPos > 0) {
this.start = Integer.parseInt(string.substring(0, dashPos));
} else {
this.start = Long.MIN_VALUE;
}
if (dashPos < length - 1) {
this.end = Integer.parseInt(string.substring(dashPos + 1, length));
} else {
this.end = Long.MAX_VALUE;
}
if (this.start > this.end) {
throw new NumberFormatException("Start value is greater than end value.");
}
}
public long getStart() {
return this.start;
}
public long getEnd() {
return this.end;
}
public long length() {
return this.end - this.start + 1;
}
public ByteRange intersection(ByteRange range) {
if (range.end < this.start || this.end < range.start) {
return null;
} else {
long start = (this.start > range.start) ? this.start : range.start;
long end = (this.end < range.end) ? this.end : range.end;
return new ByteRange(start, end);
}
}
public String toString() {
return this.start + "-" + this.end;
}
}
Finds the value in the range (start,limit) of the largest element (rank) where the count of all smaller elements in that range is less than or equals target.
/* Copyright (c) 2001-2009, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Collection of routines for counting the distribution of the values
* in an int[] array.
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.7.2
* @since 1.7.2
*/
public class ArrayCounter {
/**
* Returns an int[] array of length segments containing the distribution
* count of the elements in unsorted int[] array with values between min
* and max (range). Values outside the min-max range are ignored<p>
*
* A usage example is determining the count of people of each age group
* in a large int[] array containing the age of each person. Called with
* (array, 16,0,79), it will return an int[16] with the first element
* the count of people aged 0-4, the second element the count of those
* aged 5-9, and so on. People above the age of 79 are excluded. If the
* range is not a multiple of segments, the last segment will be cover a
* smaller sub-range than the rest.
*
*/
public static int[] countSegments(int[] array, int elements,
int segments, int start, int limit) {
int[] counts = new int[segments];
long interval = calcInterval(segments, start, limit);
int index = 0;
int element = 0;
if (interval <= 0) {
return counts;
}
for (int i = 0; i < elements; i++) {
element = array[i];
if (element < start || element >= limit) {
continue;
}
index = (int) ((element - start) / interval);
counts[index]++;
}
return counts;
}
/**
* With an unsorted int[] array and with target a positive integer in the
* range (1,array.length), finds the value in the range (start,limit) of the
* largest element (rank) where the count of all smaller elements in that
* range is less than or equals target. Parameter margin indicates the
* margin of error in target<p>
*
* In statistics, this can be used to calculate a median or quadrile value.
* A usage example applied to an array of age values is to determine
* the maximum age of a given number of people. With the example array
* given in countSegments, rank(array, c, 6000, 18, 65, 0) will return an age
* value between 18-64 (inclusive) and the count of all people aged between
* 18 and the returned value(exclusive) will be less than or equal 6000.
*
*/
public static int rank(int[] array, int elements, int target, int start,
int limit, int margin) {
final int segments = 256;
int elementCount = 0;
int currentLimit = limit;
for (;;) {
long interval = calcInterval(segments, start, currentLimit);
int[] counts = countSegments(array, elements, segments, start,
currentLimit);
for (int i = 0; i < counts.length; i++) {
if (elementCount + counts[i] < target) {
elementCount += counts[i];
start += interval;
} else {
break;
}
}
if (elementCount + margin >= target) {
return start;
}
if (interval <= 1) {
return start;
}
currentLimit = start + interval < limit ? (int) (start + interval)
: limit;
}
}
/**
* Helper method to calculate the span of the sub-interval. Simply returns
* the cieling of ((limit - start) / segments) and accounts for invalid
* start and limit combinations.
*/
static long calcInterval(int segments, int start, int limit) {
long range = limit - start;
if (range < 0) {
return 0;
}
int partSegment = (range % segments) == 0 ? 0
: 1;
return (range / segments) + partSegment;
}
}
IntRange represents an inclusive range of ints.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import java.io.Serializable;
/**
* <p><code>IntRange</code> represents an inclusive range of <code>int</code>s.</p>
*
* @author Stephen Colebourne
* @since 2.0
* @version $Id: IntRange.java 594398 2007-11-13 02:43:10Z bayard $
*/
public final class IntRange extends Range implements Serializable {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 71849363892730L;
/**
* The minimum number in this range (inclusive).
*/
private final int min;
/**
* The maximum number in this range (inclusive).
*/
private final int max;
/**
* Cached output minObject (class is immutable).
*/
private transient Integer minObject = null;
/**
* Cached output maxObject (class is immutable).
*/
private transient Integer maxObject = null;
/**
* Cached output hashCode (class is immutable).
*/
private transient int hashCode = 0;
/**
* Cached output toString (class is immutable).
*/
private transient String toString = null;
/**
* <p>Constructs a new <code>IntRange</code> using the specified
* number as both the minimum and maximum in this range.</p>
*
* @param number the number to use for this range
*/
public IntRange(int number) {
super();
this.min = number;
this.max = number;
}
/**
* <p>Constructs a new <code>IntRange</code> using the specified
* number as both the minimum and maximum in this range.</p>
*
* @param number the number to use for this range, must not be <code>null</code>
* @throws IllegalArgumentException if the number is <code>null</code>
*/
public IntRange(Number number) {
super();
if (number == null) {
throw new IllegalArgumentException("The number must not be null");
}
this.min = number.intValue();
this.max = number.intValue();
if (number instanceof Integer) {
this.minObject = (Integer) number;
this.maxObject = (Integer) number;
}
}
/**
* <p>Constructs a new <code>IntRange</code> with the specified
* minimum and maximum numbers (both inclusive).</p>
*
* <p>The arguments may be passed in the order (min,max) or (max,min). The
* getMinimum and getMaximum methods will return the correct values.</p>
*
* @param number1 first number that defines the edge of the range, inclusive
* @param number2 second number that defines the edge of the range, inclusive
*/
public IntRange(int number1, int number2) {
super();
if (number2 < number1) {
this.min = number2;
this.max = number1;
} else {
this.min = number1;
this.max = number2;
}
}
/**
* <p>Constructs a new <code>IntRange</code> with the specified
* minimum and maximum numbers (both inclusive).</p>
*
* <p>The arguments may be passed in the order (min,max) or (max,min). The
* getMinimum and getMaximum methods will return the correct values.</p>
*
* @param number1 first number that defines the edge of the range, inclusive
* @param number2 second number that defines the edge of the range, inclusive
* @throws IllegalArgumentException if either number is <code>null</code>
*/
public IntRange(Number number1, Number number2) {
super();
if (number1 == null || number2 == null) {
throw new IllegalArgumentException("The numbers must not be null");
}
int number1val = number1.intValue();
int number2val = number2.intValue();
if (number2val < number1val) {
this.min = number2val;
this.max = number1val;
if (number2 instanceof Integer) {
this.minObject = (Integer) number2;
}
if (number1 instanceof Integer) {
this.maxObject = (Integer) number1;
}
} else {
this.min = number1val;
this.max = number2val;
if (number1 instanceof Integer) {
this.minObject = (Integer) number1;
}
if (number2 instanceof Integer) {
this.maxObject = (Integer) number2;
}
}
}
// Accessors
//--------------------------------------------------------------------
/**
* <p>Returns the minimum number in this range.</p>
*
* @return the minimum number in this range
*/
public Number getMinimumNumber() {
if (minObject == null) {
minObject = new Integer(min);
}
return minObject;
}
/**
* <p>Gets the minimum number in this range as a <code>long</code>.</p>
*
* @return the minimum number in this range
*/
public long getMinimumLong() {
return min;
}
/**
* <p>Gets the minimum number in this range as a <code>int</code>.</p>
*
* @return the minimum number in this range
*/
public int getMinimumInteger() {
return min;
}
/**
* <p>Gets the minimum number in this range as a <code>double</code>.</p>
*
* @return the minimum number in this range
*/
public double getMinimumDouble() {
return min;
}
/**
* <p>Gets the minimum number in this range as a <code>float</code>.</p>
*
* @return the minimum number in this range
*/
public float getMinimumFloat() {
return min;
}
/**
* <p>Returns the maximum number in this range.</p>
*
* @return the maximum number in this range
*/
public Number getMaximumNumber() {
if (maxObject == null) {
maxObject = new Integer(max);
}
return maxObject;
}
/**
* <p>Gets the maximum number in this range as a <code>long</code>.</p>
*
* @return the maximum number in this range
*/
public long getMaximumLong() {
return max;
}
/**
* <p>Gets the maximum number in this range as a <code>int</code>.</p>
*
* @return the maximum number in this range
*/
public int getMaximumInteger() {
return max;
}
/**
* <p>Gets the maximum number in this range as a <code>double</code>.</p>
*
* @return the maximum number in this range
*/
public double getMaximumDouble() {
return max;
}
/**
* <p>Gets the maximum number in this range as a <code>float</code>.</p>
*
* @return the maximum number in this range
*/
public float getMaximumFloat() {
return max;
}
// Tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified <code>number</code> occurs within
* this range using <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param number the number to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this range
*/
public boolean containsNumber(Number number) {
if (number == null) {
return false;
}
return containsInteger(number.intValue());
}
/**
* <p>Tests whether the specified <code>int</code> occurs within
* this range using <code>int</code> comparison.</p>
*
* <p>This implementation overrides the superclass for performance as it is
* the most common case.</p>
*
* @param value the int to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>int</code> comparison
*/
public boolean containsInteger(int value) {
return value >= min && value <= max;
}
// Range tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified range occurs entirely within this range
* using <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range occurs entirely within this range
* @throws IllegalArgumentException if the range is not of this type
*/
public boolean containsRange(Range range) {
if (range == null) {
return false;
}
return containsInteger(range.getMinimumInteger()) &&
containsInteger(range.getMaximumInteger());
}
/**
* <p>Tests whether the specified range overlaps with this range
* using <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range overlaps with this range
*/
public boolean overlapsRange(Range range) {
if (range == null) {
return false;
}
return range.containsInteger(min) ||
range.containsInteger(max) ||
containsInteger(range.getMinimumInteger());
}
// Basics
//--------------------------------------------------------------------
/**
* <p>Compares this range to another object to test if they are equal.</p>.
*
* <p>To be equal, the class, minimum and maximum must be equal.</p>
*
* @param obj the reference object with which to compare
* @return <code>true</code> if this object is equal
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof IntRange == false) {
return false;
}
IntRange range = (IntRange) obj;
return min == range.min && max == range.max;
}
/**
* <p>Gets a hashCode for the range.</p>
*
* @return a hash code value for this object
*/
public int hashCode() {
if (hashCode == 0) {
hashCode = 17;
hashCode = 37 * hashCode + getClass().hashCode();
hashCode = 37 * hashCode + min;
hashCode = 37 * hashCode + max;
}
return hashCode;
}
/**
* <p>Gets the range as a <code>String</code>.</p>
*
* <p>The format of the String is "Range[<i>min</i>,<i>max</i>]".</p>
*
* @return the <code>String</code> representation of this range
*/
public String toString() {
if (toString == null) {
StringBuffer buf = new StringBuffer(32);
buf.append("Range[");
buf.append(min);
buf.append(",");
buf.append(max);
buf.append("]");
toString = buf.toString();
}
return toString;
}
/**
* <p>Returns an array containing all the integer values in the range.</p>
*
* @return the <code>int[]</code> representation of this range
* @since 2.4
*/
public int[] toArray() {
int[] array = new int[max - min + 1];
for (int i = 0; i < array.length; i++) {
array[i] = min + i;
}
return array;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* <p><code>Range</code> represents a range of numbers of the same type.</p>
*
* <p>Specific subclasses hold the range values as different types. Each
* subclass should be immutable and {@link java.io.Serializable Serializable}
* if possible.</p>
*
* @author Stephen Colebourne
* @since 2.0
* @version $Id: Range.java 437554 2006-08-28 06:21:41Z bayard $
*/
abstract class Range {
/**
* <p>Constructs a new range.</p>
*/
public Range() {
super();
}
// Accessors
//--------------------------------------------------------------------
/**
* <p>Gets the minimum number in this range.</p>
*
* @return the minimum number in this range
*/
public abstract Number getMinimumNumber();
/**
* <p>Gets the minimum number in this range as a <code>long</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public long getMinimumLong() {
return getMinimumNumber().longValue();
}
/**
* <p>Gets the minimum number in this range as a <code>int</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public int getMinimumInteger() {
return getMinimumNumber().intValue();
}
/**
* <p>Gets the minimum number in this range as a <code>double</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public double getMinimumDouble() {
return getMinimumNumber().doubleValue();
}
/**
* <p>Gets the minimum number in this range as a <code>float</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public float getMinimumFloat() {
return getMinimumNumber().floatValue();
}
/**
* <p>Gets the maximum number in this range.</p>
*
* @return the maximum number in this range
*/
public abstract Number getMaximumNumber();
/**
* <p>Gets the maximum number in this range as a <code>long</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public long getMaximumLong() {
return getMaximumNumber().longValue();
}
/**
* <p>Gets the maximum number in this range as a <code>int</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public int getMaximumInteger() {
return getMaximumNumber().intValue();
}
/**
* <p>Gets the maximum number in this range as a <code>double</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public double getMaximumDouble() {
return getMaximumNumber().doubleValue();
}
/**
* <p>Gets the maximum number in this range as a <code>float</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public float getMaximumFloat() {
return getMaximumNumber().floatValue();
}
// Include tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param number the number to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this range
* @throws IllegalArgumentException if the <code>Number</code> cannot be compared
*/
public abstract boolean containsNumber(Number number);
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>long</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsLong(long)} method.</p>
*
* @param value the long to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>long</code> comparison
*/
public boolean containsLong(Number value) {
if (value == null) {
return false;
}
return containsLong(value.longValue());
}
/**
* <p>Tests whether the specified <code>long</code> occurs within
* this range using <code>long</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumLong()} and
* {@link #getMaximumLong()} methods and should be good for most uses.</p>
*
* @param value the long to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>long</code> comparison
*/
public boolean containsLong(long value) {
return value >= getMinimumLong() && value <= getMaximumLong();
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>int</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsInteger(int)} method.</p>
*
* @param value the integer to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>int</code> comparison
*/
public boolean containsInteger(Number value) {
if (value == null) {
return false;
}
return containsInteger(value.intValue());
}
/**
* <p>Tests whether the specified <code>int</code> occurs within
* this range using <code>int</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumInteger()} and
* {@link #getMaximumInteger()} methods and should be good for most uses.</p>
*
* @param value the int to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>int</code> comparison
*/
public boolean containsInteger(int value) {
return value >= getMinimumInteger() && value <= getMaximumInteger();
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>double</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsDouble(double)} method.</p>
*
* @param value the double to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>double</code> comparison
*/
public boolean containsDouble(Number value) {
if (value == null) {
return false;
}
return containsDouble(value.doubleValue());
}
/**
* <p>Tests whether the specified <code>double</code> occurs within
* this range using <code>double</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumDouble()} and
* {@link #getMaximumDouble()} methods and should be good for most uses.</p>
*
* @param value the double to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>double</code> comparison
*/
public boolean containsDouble(double value) {
int compareMin = compare(getMinimumDouble(), value);
int compareMax = compare(getMaximumDouble(), value);
return compareMin <= 0 && compareMax >= 0;
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>float</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsFloat(float)} method.</p>
*
* @param value the float to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>float</code> comparison
*/
public boolean containsFloat(Number value) {
if (value == null) {
return false;
}
return containsFloat(value.floatValue());
}
/**
* <p>Tests whether the specified <code>float</code> occurs within
* this range using <code>float</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumFloat()} and
* {@link #getMaximumFloat()} methods and should be good for most uses.</p>
*
* @param value the float to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>float</code> comparison
*/
public boolean containsFloat(float value) {
int compareMin = compare(getMinimumFloat(), value);
int compareMax = compare(getMaximumFloat(), value);
return compareMin <= 0 && compareMax >= 0;
}
// Range tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified range occurs entirely within this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation uses the {@link #containsNumber(Number)} method.
* Subclasses may be able to optimise this.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range occurs entirely within
* this range; otherwise, <code>false</code>
* @throws IllegalArgumentException if the <code>Range</code> cannot be compared
*/
public boolean containsRange(Range range) {
if (range == null) {
return false;
}
return containsNumber(range.getMinimumNumber())
&& containsNumber(range.getMaximumNumber());
}
/**
* <p>Tests whether the specified range overlaps with this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation uses the {@link #containsNumber(Number)} and
* {@link #containsRange(Range)} methods.
* Subclasses may be able to optimise this.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range overlaps with this
* range; otherwise, <code>false</code>
* @throws IllegalArgumentException if the <code>Range</code> cannot be compared
*/
public boolean overlapsRange(Range range) {
if (range == null) {
return false;
}
return range.containsNumber(getMinimumNumber())
|| range.containsNumber(getMaximumNumber())
|| containsNumber(range.getMinimumNumber());
}
// Basics
//--------------------------------------------------------------------
/**
* <p>Compares this range to another object to test if they are equal.</p>.
*
* <p>To be equal, the class, minimum and maximum must be equal.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @param obj the reference object with which to compare
* @return <code>true</code> if this object is equal
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj == null || obj.getClass() != getClass()) {
return false;
} else {
Range range = (Range) obj;
return getMinimumNumber().equals(range.getMinimumNumber()) &&
getMaximumNumber().equals(range.getMaximumNumber());
}
}
/**
* <p>Gets a hashCode for the range.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @return a hash code value for this object
*/
public int hashCode() {
int result = 17;
result = 37 * result + getClass().hashCode();
result = 37 * result + getMinimumNumber().hashCode();
result = 37 * result + getMaximumNumber().hashCode();
return result;
}
/**
* <p>Gets the range as a <code>String</code>.</p>
*
* <p>The format of the String is "Range[<i>min</i>,<i>max</i>]".</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @return the <code>String</code> representation of this range
*/
public String toString() {
StringBuffer buf = new StringBuffer(32);
buf.append("Range[");
buf.append(getMinimumNumber());
buf.append(",");
buf.append(getMaximumNumber());
buf.append("]");
return buf.toString();
}
/**
* <p>Compares two <code>doubles</code> for order.</p>
*
* <p>This method is more comprehensive than the standard Java greater
* than, less than and equals operators.</p>
* <ul>
* <li>It returns <code>-1</code> if the first value is less than the second.</li>
* <li>It returns <code>+1</code> if the first value is greater than the second.</li>
* <li>It returns <code>0</code> if the values are equal.</li>
* </ul>
*
* <p>
* The ordering is as follows, largest to smallest:
* <ul>
* <li>NaN
* <li>Positive infinity
* <li>Maximum double
* <li>Normal positive numbers
* <li>+0.0
* <li>-0.0
* <li>Normal negative numbers
* <li>Minimum double (<code>-Double.MAX_VALUE</code>)
* <li>Negative infinity
* </ul>
* </p>
*
* <p>Comparing <code>NaN</code> with <code>NaN</code> will
* return <code>0</code>.</p>
*
* @param lhs the first <code>double</code>
* @param rhs the second <code>double</code>
* @return <code>-1</code> if lhs is less, <code>+1</code> if greater,
* <code>0</code> if equal to rhs
*/
public static int compare(double lhs, double rhs) {
if (lhs < rhs) {
return -1;
}
if (lhs > rhs) {
return +1;
}
// Need to compare bits to handle 0.0 == -0.0 being true
// compare should put -0.0 < +0.0
// Two NaNs are also == for compare purposes
// where NaN == NaN is false
long lhsBits = Double.doubleToLongBits(lhs);
long rhsBits = Double.doubleToLongBits(rhs);
if (lhsBits == rhsBits) {
return 0;
}
// Something exotic! A comparison to NaN or 0.0 vs -0.0
// Fortunately NaN"s long is > than everything else
// Also negzeros bits < poszero
// NAN: 9221120237041090560
// MAX: 9218868437227405311
// NEGZERO: -9223372036854775808
if (lhsBits < rhsBits) {
return -1;
} else {
return +1;
}
}
/**
* <p>Compares two floats for order.</p>
*
* <p>This method is more comprehensive than the standard Java greater than,
* less than and equals operators.</p>
* <ul>
* <li>It returns <code>-1</code> if the first value is less than the second.
* <li>It returns <code>+1</code> if the first value is greater than the second.
* <li>It returns <code>0</code> if the values are equal.
* </ul>
*
* <p> The ordering is as follows, largest to smallest:
* <ul>
* <li>NaN
* <li>Positive infinity
* <li>Maximum float
* <li>Normal positive numbers
* <li>+0.0
* <li>-0.0
* <li>Normal negative numbers
* <li>Minimum float (<code>-Float.MAX_VALUE</code>)
* <li>Negative infinity
* </ul>
*
* <p>Comparing <code>NaN</code> with <code>NaN</code> will return
* <code>0</code>.</p>
*
* @param lhs the first <code>float</code>
* @param rhs the second <code>float</code>
* @return <code>-1</code> if lhs is less, <code>+1</code> if greater,
* <code>0</code> if equal to rhs
*/
public static int compare(float lhs, float rhs) {
if (lhs < rhs) {
return -1;
}
if (lhs > rhs) {
return +1;
}
//Need to compare bits to handle 0.0 == -0.0 being true
// compare should put -0.0 < +0.0
// Two NaNs are also == for compare purposes
// where NaN == NaN is false
int lhsBits = Float.floatToIntBits(lhs);
int rhsBits = Float.floatToIntBits(rhs);
if (lhsBits == rhsBits) {
return 0;
}
//Something exotic! A comparison to NaN or 0.0 vs -0.0
//Fortunately NaN"s int is > than everything else
//Also negzeros bits < poszero
//NAN: 2143289344
//MAX: 2139095039
//NEGZERO: -2147483648
if (lhsBits < rhsBits) {
return -1;
} else {
return +1;
}
}
}
LongRange represents an inclusive range of longs.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import java.io.Serializable;
/**
* <p><code>LongRange</code> represents an inclusive range of <code>long</code>s.</p>
*
* @author Stephen Colebourne
* @since 2.0
* @version $Id: LongRange.java 594398 2007-11-13 02:43:10Z bayard $
*/
public final class LongRange extends Range implements Serializable {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 71849363892720L;
/**
* The minimum number in this range (inclusive).
*/
private final long min;
/**
* The maximum number in this range (inclusive).
*/
private final long max;
/**
* Cached output minObject (class is immutable).
*/
private transient Long minObject = null;
/**
* Cached output maxObject (class is immutable).
*/
private transient Long maxObject = null;
/**
* Cached output hashCode (class is immutable).
*/
private transient int hashCode = 0;
/**
* Cached output toString (class is immutable).
*/
private transient String toString = null;
/**
* <p>Constructs a new <code>LongRange</code> using the specified
* number as both the minimum and maximum in this range.</p>
*
* @param number the number to use for this range
*/
public LongRange(long number) {
super();
this.min = number;
this.max = number;
}
/**
* <p>Constructs a new <code>LongRange</code> using the specified
* number as both the minimum and maximum in this range.</p>
*
* @param number the number to use for this range, must not
* be <code>null</code>
* @throws IllegalArgumentException if the number is <code>null</code>
*/
public LongRange(Number number) {
super();
if (number == null) {
throw new IllegalArgumentException("The number must not be null");
}
this.min = number.longValue();
this.max = number.longValue();
if (number instanceof Long) {
this.minObject = (Long) number;
this.maxObject = (Long) number;
}
}
/**
* <p>Constructs a new <code>LongRange</code> with the specified
* minimum and maximum numbers (both inclusive).</p>
*
* <p>The arguments may be passed in the order (min,max) or (max,min). The
* getMinimum and getMaximum methods will return the correct values.</p>
*
* @param number1 first number that defines the edge of the range, inclusive
* @param number2 second number that defines the edge of the range, inclusive
*/
public LongRange(long number1, long number2) {
super();
if (number2 < number1) {
this.min = number2;
this.max = number1;
} else {
this.min = number1;
this.max = number2;
}
}
/**
* <p>Constructs a new <code>LongRange</code> with the specified
* minimum and maximum numbers (both inclusive).</p>
*
* <p>The arguments may be passed in the order (min,max) or (max,min). The
* getMinimum and getMaximum methods will return the correct values.</p>
*
* @param number1 first number that defines the edge of the range, inclusive
* @param number2 second number that defines the edge of the range, inclusive
* @throws IllegalArgumentException if either number is <code>null</code>
*/
public LongRange(Number number1, Number number2) {
super();
if (number1 == null || number2 == null) {
throw new IllegalArgumentException("The numbers must not be null");
}
long number1val = number1.longValue();
long number2val = number2.longValue();
if (number2val < number1val) {
this.min = number2val;
this.max = number1val;
if (number2 instanceof Long) {
this.minObject = (Long) number2;
}
if (number1 instanceof Long) {
this.maxObject = (Long) number1;
}
} else {
this.min = number1val;
this.max = number2val;
if (number1 instanceof Long) {
this.minObject = (Long) number1;
}
if (number2 instanceof Long) {
this.maxObject = (Long) number2;
}
}
}
// Accessors
//--------------------------------------------------------------------
/**
* <p>Returns the minimum number in this range.</p>
*
* @return the minimum number in this range
*/
public Number getMinimumNumber() {
if (minObject == null) {
minObject = new Long(min);
}
return minObject;
}
/**
* <p>Gets the minimum number in this range as a <code>long</code>.</p>
*
* @return the minimum number in this range
*/
public long getMinimumLong() {
return min;
}
/**
* <p>Gets the minimum number in this range as a <code>int</code>.</p>
*
* <p>This conversion can lose information for large values.</p>
*
* @return the minimum number in this range
*/
public int getMinimumInteger() {
return (int) min;
}
/**
* <p>Gets the minimum number in this range as a <code>double</code>.</p>
*
* <p>This conversion can lose information for large values.</p>
*
* @return the minimum number in this range
*/
public double getMinimumDouble() {
return min;
}
/**
* <p>Gets the minimum number in this range as a <code>float</code>.</p>
*
* <p>This conversion can lose information for large values.</p>
*
* @return the minimum number in this range
*/
public float getMinimumFloat() {
return min;
}
/**
* <p>Returns the maximum number in this range.</p>
*
* @return the maximum number in this range
*/
public Number getMaximumNumber() {
if (maxObject == null) {
maxObject = new Long(max);
}
return maxObject;
}
/**
* <p>Gets the maximum number in this range as a <code>long</code>.</p>
*
* @return the maximum number in this range
*/
public long getMaximumLong() {
return max;
}
/**
* <p>Gets the maximum number in this range cast to an <code>int</code>.</p>
*
* <p>This conversion can lose information for large values.</p>
*
* @return the maximum number in this range cast to an <code>int</code>.
*/
public int getMaximumInteger() {
return (int) max;
}
/**
* <p>Gets the maximum number in this range as a <code>double</code>.</p>
*
* <p>This conversion can lose information for large values.</p>
*
* @return The maximum number in this range as a <code>double</code>.
*/
public double getMaximumDouble() {
return max;
}
/**
* <p>Gets the maximum number in this range as a <code>float</code>.</p>
*
* <p>This conversion can lose information for large values.</p>
*
* @return The maximum number in this range as a <code>float</code>.
*/
public float getMaximumFloat() {
return max;
}
// Tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified <code>number</code> occurs within
* this range using <code>long</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param number the number to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this range
*/
public boolean containsNumber(Number number) {
if (number == null) {
return false;
}
return containsLong(number.longValue());
}
/**
* <p>Tests whether the specified <code>long</code> occurs within
* this range using <code>long</code> comparison.</p>
*
* <p>This implementation overrides the superclass for performance as it is
* the most common case.</p>
*
* @param value the long to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>long</code> comparison
*/
public boolean containsLong(long value) {
return value >= min && value <= max;
}
// Range tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified range occurs entirely within this range
* using <code>long</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range occurs entirely within this range
* @throws IllegalArgumentException if the range is not of this type
*/
public boolean containsRange(Range range) {
if (range == null) {
return false;
}
return containsLong(range.getMinimumLong()) &&
containsLong(range.getMaximumLong());
}
/**
* <p>Tests whether the specified range overlaps with this range
* using <code>long</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range overlaps with this range
*/
public boolean overlapsRange(Range range) {
if (range == null) {
return false;
}
return range.containsLong(min) ||
range.containsLong(max) ||
containsLong(range.getMinimumLong());
}
// Basics
//--------------------------------------------------------------------
/**
* <p>Compares this range to another object to test if they are equal.</p>.
*
* <p>To be equal, the class, minimum and maximum must be equal.</p>
*
* @param obj the reference object with which to compare
* @return <code>true</code> if this object is equal
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof LongRange == false) {
return false;
}
LongRange range = (LongRange) obj;
return min == range.min && max == range.max;
}
/**
* <p>Gets a hashCode for the range.</p>
*
* @return a hash code value for this object
*/
public int hashCode() {
if (hashCode == 0) {
hashCode = 17;
hashCode = 37 * hashCode + getClass().hashCode();
hashCode = 37 * hashCode + ((int) (min ^ (min >> 32)));
hashCode = 37 * hashCode + ((int) (max ^ (max >> 32)));
}
return hashCode;
}
/**
* <p>Gets the range as a <code>String</code>.</p>
*
* <p>The format of the String is "Range[<i>min</i>,<i>max</i>]".</p>
*
* @return the <code>String</code> representation of this range
*/
public String toString() {
if (toString == null) {
StringBuffer buf = new StringBuffer(32);
buf.append("Range[");
buf.append(min);
buf.append(",");
buf.append(max);
buf.append("]");
toString = buf.toString();
}
return toString;
}
/**
* <p>Returns an array containing all the long values in the range.</p>
*
* @return the <code>long[]</code> representation of this range
* @since 2.4
*/
public long[] toArray() {
long[] array = new long[(int)(max - min + 1L)];
for(int i = 0; i < array.length; i++) {
array[i] = min + i;
}
return array;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* <p><code>Range</code> represents a range of numbers of the same type.</p>
*
* <p>Specific subclasses hold the range values as different types. Each
* subclass should be immutable and {@link java.io.Serializable Serializable}
* if possible.</p>
*
* @author Stephen Colebourne
* @since 2.0
* @version $Id: Range.java 437554 2006-08-28 06:21:41Z bayard $
*/
abstract class Range {
/**
* <p>Constructs a new range.</p>
*/
public Range() {
super();
}
// Accessors
//--------------------------------------------------------------------
/**
* <p>Gets the minimum number in this range.</p>
*
* @return the minimum number in this range
*/
public abstract Number getMinimumNumber();
/**
* <p>Gets the minimum number in this range as a <code>long</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public long getMinimumLong() {
return getMinimumNumber().longValue();
}
/**
* <p>Gets the minimum number in this range as a <code>int</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public int getMinimumInteger() {
return getMinimumNumber().intValue();
}
/**
* <p>Gets the minimum number in this range as a <code>double</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public double getMinimumDouble() {
return getMinimumNumber().doubleValue();
}
/**
* <p>Gets the minimum number in this range as a <code>float</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public float getMinimumFloat() {
return getMinimumNumber().floatValue();
}
/**
* <p>Gets the maximum number in this range.</p>
*
* @return the maximum number in this range
*/
public abstract Number getMaximumNumber();
/**
* <p>Gets the maximum number in this range as a <code>long</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public long getMaximumLong() {
return getMaximumNumber().longValue();
}
/**
* <p>Gets the maximum number in this range as a <code>int</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public int getMaximumInteger() {
return getMaximumNumber().intValue();
}
/**
* <p>Gets the maximum number in this range as a <code>double</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public double getMaximumDouble() {
return getMaximumNumber().doubleValue();
}
/**
* <p>Gets the maximum number in this range as a <code>float</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public float getMaximumFloat() {
return getMaximumNumber().floatValue();
}
// Include tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param number the number to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this range
* @throws IllegalArgumentException if the <code>Number</code> cannot be compared
*/
public abstract boolean containsNumber(Number number);
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>long</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsLong(long)} method.</p>
*
* @param value the long to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>long</code> comparison
*/
public boolean containsLong(Number value) {
if (value == null) {
return false;
}
return containsLong(value.longValue());
}
/**
* <p>Tests whether the specified <code>long</code> occurs within
* this range using <code>long</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumLong()} and
* {@link #getMaximumLong()} methods and should be good for most uses.</p>
*
* @param value the long to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>long</code> comparison
*/
public boolean containsLong(long value) {
return value >= getMinimumLong() && value <= getMaximumLong();
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>int</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsInteger(int)} method.</p>
*
* @param value the integer to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>int</code> comparison
*/
public boolean containsInteger(Number value) {
if (value == null) {
return false;
}
return containsInteger(value.intValue());
}
/**
* <p>Tests whether the specified <code>int</code> occurs within
* this range using <code>int</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumInteger()} and
* {@link #getMaximumInteger()} methods and should be good for most uses.</p>
*
* @param value the int to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>int</code> comparison
*/
public boolean containsInteger(int value) {
return value >= getMinimumInteger() && value <= getMaximumInteger();
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>double</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsDouble(double)} method.</p>
*
* @param value the double to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>double</code> comparison
*/
public boolean containsDouble(Number value) {
if (value == null) {
return false;
}
return containsDouble(value.doubleValue());
}
/**
* <p>Tests whether the specified <code>double</code> occurs within
* this range using <code>double</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumDouble()} and
* {@link #getMaximumDouble()} methods and should be good for most uses.</p>
*
* @param value the double to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>double</code> comparison
*/
public boolean containsDouble(double value) {
int compareMin = compare(getMinimumDouble(), value);
int compareMax = compare(getMaximumDouble(), value);
return compareMin <= 0 && compareMax >= 0;
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>float</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsFloat(float)} method.</p>
*
* @param value the float to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>float</code> comparison
*/
public boolean containsFloat(Number value) {
if (value == null) {
return false;
}
return containsFloat(value.floatValue());
}
/**
* <p>Tests whether the specified <code>float</code> occurs within
* this range using <code>float</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumFloat()} and
* {@link #getMaximumFloat()} methods and should be good for most uses.</p>
*
* @param value the float to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>float</code> comparison
*/
public boolean containsFloat(float value) {
int compareMin = compare(getMinimumFloat(), value);
int compareMax = compare(getMaximumFloat(), value);
return compareMin <= 0 && compareMax >= 0;
}
// Range tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified range occurs entirely within this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation uses the {@link #containsNumber(Number)} method.
* Subclasses may be able to optimise this.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range occurs entirely within
* this range; otherwise, <code>false</code>
* @throws IllegalArgumentException if the <code>Range</code> cannot be compared
*/
public boolean containsRange(Range range) {
if (range == null) {
return false;
}
return containsNumber(range.getMinimumNumber())
&& containsNumber(range.getMaximumNumber());
}
/**
* <p>Tests whether the specified range overlaps with this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation uses the {@link #containsNumber(Number)} and
* {@link #containsRange(Range)} methods.
* Subclasses may be able to optimise this.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range overlaps with this
* range; otherwise, <code>false</code>
* @throws IllegalArgumentException if the <code>Range</code> cannot be compared
*/
public boolean overlapsRange(Range range) {
if (range == null) {
return false;
}
return range.containsNumber(getMinimumNumber())
|| range.containsNumber(getMaximumNumber())
|| containsNumber(range.getMinimumNumber());
}
// Basics
//--------------------------------------------------------------------
/**
* <p>Compares this range to another object to test if they are equal.</p>.
*
* <p>To be equal, the class, minimum and maximum must be equal.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @param obj the reference object with which to compare
* @return <code>true</code> if this object is equal
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj == null || obj.getClass() != getClass()) {
return false;
} else {
Range range = (Range) obj;
return getMinimumNumber().equals(range.getMinimumNumber()) &&
getMaximumNumber().equals(range.getMaximumNumber());
}
}
/**
* <p>Gets a hashCode for the range.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @return a hash code value for this object
*/
public int hashCode() {
int result = 17;
result = 37 * result + getClass().hashCode();
result = 37 * result + getMinimumNumber().hashCode();
result = 37 * result + getMaximumNumber().hashCode();
return result;
}
/**
* <p>Gets the range as a <code>String</code>.</p>
*
* <p>The format of the String is "Range[<i>min</i>,<i>max</i>]".</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @return the <code>String</code> representation of this range
*/
public String toString() {
StringBuffer buf = new StringBuffer(32);
buf.append("Range[");
buf.append(getMinimumNumber());
buf.append(",");
buf.append(getMaximumNumber());
buf.append("]");
return buf.toString();
}
/**
* <p>Compares two <code>doubles</code> for order.</p>
*
* <p>This method is more comprehensive than the standard Java greater
* than, less than and equals operators.</p>
* <ul>
* <li>It returns <code>-1</code> if the first value is less than the second.</li>
* <li>It returns <code>+1</code> if the first value is greater than the second.</li>
* <li>It returns <code>0</code> if the values are equal.</li>
* </ul>
*
* <p>
* The ordering is as follows, largest to smallest:
* <ul>
* <li>NaN
* <li>Positive infinity
* <li>Maximum double
* <li>Normal positive numbers
* <li>+0.0
* <li>-0.0
* <li>Normal negative numbers
* <li>Minimum double (<code>-Double.MAX_VALUE</code>)
* <li>Negative infinity
* </ul>
* </p>
*
* <p>Comparing <code>NaN</code> with <code>NaN</code> will
* return <code>0</code>.</p>
*
* @param lhs the first <code>double</code>
* @param rhs the second <code>double</code>
* @return <code>-1</code> if lhs is less, <code>+1</code> if greater,
* <code>0</code> if equal to rhs
*/
public static int compare(double lhs, double rhs) {
if (lhs < rhs) {
return -1;
}
if (lhs > rhs) {
return +1;
}
// Need to compare bits to handle 0.0 == -0.0 being true
// compare should put -0.0 < +0.0
// Two NaNs are also == for compare purposes
// where NaN == NaN is false
long lhsBits = Double.doubleToLongBits(lhs);
long rhsBits = Double.doubleToLongBits(rhs);
if (lhsBits == rhsBits) {
return 0;
}
// Something exotic! A comparison to NaN or 0.0 vs -0.0
// Fortunately NaN"s long is > than everything else
// Also negzeros bits < poszero
// NAN: 9221120237041090560
// MAX: 9218868437227405311
// NEGZERO: -9223372036854775808
if (lhsBits < rhsBits) {
return -1;
} else {
return +1;
}
}
/**
* <p>Compares two floats for order.</p>
*
* <p>This method is more comprehensive than the standard Java greater than,
* less than and equals operators.</p>
* <ul>
* <li>It returns <code>-1</code> if the first value is less than the second.
* <li>It returns <code>+1</code> if the first value is greater than the second.
* <li>It returns <code>0</code> if the values are equal.
* </ul>
*
* <p> The ordering is as follows, largest to smallest:
* <ul>
* <li>NaN
* <li>Positive infinity
* <li>Maximum float
* <li>Normal positive numbers
* <li>+0.0
* <li>-0.0
* <li>Normal negative numbers
* <li>Minimum float (<code>-Float.MAX_VALUE</code>)
* <li>Negative infinity
* </ul>
*
* <p>Comparing <code>NaN</code> with <code>NaN</code> will return
* <code>0</code>.</p>
*
* @param lhs the first <code>float</code>
* @param rhs the second <code>float</code>
* @return <code>-1</code> if lhs is less, <code>+1</code> if greater,
* <code>0</code> if equal to rhs
*/
public static int compare(float lhs, float rhs) {
if (lhs < rhs) {
return -1;
}
if (lhs > rhs) {
return +1;
}
//Need to compare bits to handle 0.0 == -0.0 being true
// compare should put -0.0 < +0.0
// Two NaNs are also == for compare purposes
// where NaN == NaN is false
int lhsBits = Float.floatToIntBits(lhs);
int rhsBits = Float.floatToIntBits(rhs);
if (lhsBits == rhsBits) {
return 0;
}
//Something exotic! A comparison to NaN or 0.0 vs -0.0
//Fortunately NaN"s int is > than everything else
//Also negzeros bits < poszero
//NAN: 2143289344
//MAX: 2139095039
//NEGZERO: -2147483648
if (lhsBits < rhsBits) {
return -1;
} else {
return +1;
}
}
}
NumberRange represents an inclusive range of java.lang.Number objects of the same type.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import java.io.Serializable;
/**
* <p><code>NumberRange</code> represents an inclusive range of
* {@link java.lang.Number} objects of the same type.</p>
*
* @author
* @author Stephen Colebourne
* @since 2.0 (previously in org.apache.rumons.lang)
* @version $Id: NumberRange.java 437554 2006-08-28 06:21:41Z bayard $
*/
public final class NumberRange extends Range implements Serializable {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 71849363892710L;
/**
* The minimum number in this range.
*/
private final Number min;
/**
* The maximum number in this range.
*/
private final Number max;
/**
* Cached output hashCode (class is immutable).
*/
private transient int hashCode = 0;
/**
* Cached output toString (class is immutable).
*/
private transient String toString = null;
/**
* <p>Constructs a new <code>NumberRange</code> using the specified
* number as both the minimum and maximum in this range.</p>
*
* @param num the number to use for this range
* @throws IllegalArgumentException if the number is <code>null</code>
* @throws IllegalArgumentException if the number doesn"t implement <code>Comparable</code>
* @throws IllegalArgumentException if the number is <code>Double.NaN</code> or <code>Float.NaN</code>
*/
public NumberRange(Number num) {
if (num == null) {
throw new IllegalArgumentException("The number must not be null");
}
if (num instanceof Comparable == false) {
throw new IllegalArgumentException("The number must implement Comparable");
}
if (num instanceof Double && ((Double) num).isNaN()) {
throw new IllegalArgumentException("The number must not be NaN");
}
if (num instanceof Float && ((Float) num).isNaN()) {
throw new IllegalArgumentException("The number must not be NaN");
}
this.min = num;
this.max = num;
}
/**
* <p>Constructs a new <code>NumberRange</code> with the specified
* minimum and maximum numbers (both inclusive).</p>
*
* <p>The arguments may be passed in the order (min,max) or (max,min). The
* {@link #getMinimumNumber()} and {@link #getMaximumNumber()} methods will return the
* correct value.</p>
*
* <p>This constructor is designed to be used with two <code>Number</code>
* objects of the same type. If two objects of different types are passed in,
* an exception is thrown.</p>
*
* @param num1 first number that defines the edge of the range, inclusive
* @param num2 second number that defines the edge of the range, inclusive
* @throws IllegalArgumentException if either number is <code>null</code>
* @throws IllegalArgumentException if the numbers are of different types
* @throws IllegalArgumentException if the numbers don"t implement <code>Comparable</code>
*/
public NumberRange(Number num1, Number num2) {
if (num1 == null || num2 == null) {
throw new IllegalArgumentException("The numbers must not be null");
}
if (num1.getClass() != num2.getClass()) {
throw new IllegalArgumentException("The numbers must be of the same type");
}
if (num1 instanceof Comparable == false) {
throw new IllegalArgumentException("The numbers must implement Comparable");
}
if (num1 instanceof Double) {
if (((Double) num1).isNaN() || ((Double) num2).isNaN()) {
throw new IllegalArgumentException("The number must not be NaN");
}
} else if (num1 instanceof Float) {
if (((Float) num1).isNaN() || ((Float) num2).isNaN()) {
throw new IllegalArgumentException("The number must not be NaN");
}
}
int compare = ((Comparable) num1).rupareTo(num2);
if (compare == 0) {
this.min = num1;
this.max = num1;
} else if (compare > 0) {
this.min = num2;
this.max = num1;
} else {
this.min = num1;
this.max = num2;
}
}
// Accessors
//--------------------------------------------------------------------
/**
* <p>Returns the minimum number in this range.</p>
*
* @return the minimum number in this range
*/
public Number getMinimumNumber() {
return min;
}
/**
* <p>Returns the maximum number in this range.</p>
*
* @return the maximum number in this range
*/
public Number getMaximumNumber() {
return max;
}
// Tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified <code>number</code> occurs within
* this range.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param number the number to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this range
* @throws IllegalArgumentException if the number is of a different type to the range
*/
public boolean containsNumber(Number number) {
if (number == null) {
return false;
}
if (number.getClass() != min.getClass()) {
throw new IllegalArgumentException("The number must be of the same type as the range numbers");
}
int compareMin = ((Comparable) min).rupareTo(number);
int compareMax = ((Comparable) max).rupareTo(number);
return compareMin <= 0 && compareMax >= 0;
}
// Range tests
//--------------------------------------------------------------------
// use Range implementations
// Basics
//--------------------------------------------------------------------
/**
* <p>Compares this range to another object to test if they are equal.</p>.
*
* <p>To be equal, the class, minimum and maximum must be equal.</p>
*
* @param obj the reference object with which to compare
* @return <code>true</code> if this object is equal
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof NumberRange == false) {
return false;
}
NumberRange range = (NumberRange) obj;
return min.equals(range.min) && max.equals(range.max);
}
/**
* <p>Gets a hashCode for the range.</p>
*
* @return a hash code value for this object
*/
public int hashCode() {
if (hashCode == 0) {
hashCode = 17;
hashCode = 37 * hashCode + getClass().hashCode();
hashCode = 37 * hashCode + min.hashCode();
hashCode = 37 * hashCode + max.hashCode();
}
return hashCode;
}
/**
* <p>Gets the range as a <code>String</code>.</p>
*
* <p>The format of the String is "Range[<i>min</i>,<i>max</i>]".</p>
*
* @return the <code>String</code> representation of this range
*/
public String toString() {
if (toString == null) {
StringBuffer buf = new StringBuffer(32);
buf.append("Range[");
buf.append(min);
buf.append(",");
buf.append(max);
buf.append("]");
toString = buf.toString();
}
return toString;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* <p><code>Range</code> represents a range of numbers of the same type.</p>
*
* <p>Specific subclasses hold the range values as different types. Each
* subclass should be immutable and {@link java.io.Serializable Serializable}
* if possible.</p>
*
* @author Stephen Colebourne
* @since 2.0
* @version $Id: Range.java 437554 2006-08-28 06:21:41Z bayard $
*/
abstract class Range {
/**
* <p>Constructs a new range.</p>
*/
public Range() {
super();
}
// Accessors
//--------------------------------------------------------------------
/**
* <p>Gets the minimum number in this range.</p>
*
* @return the minimum number in this range
*/
public abstract Number getMinimumNumber();
/**
* <p>Gets the minimum number in this range as a <code>long</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public long getMinimumLong() {
return getMinimumNumber().longValue();
}
/**
* <p>Gets the minimum number in this range as a <code>int</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public int getMinimumInteger() {
return getMinimumNumber().intValue();
}
/**
* <p>Gets the minimum number in this range as a <code>double</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public double getMinimumDouble() {
return getMinimumNumber().doubleValue();
}
/**
* <p>Gets the minimum number in this range as a <code>float</code>.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the minimum number in this range
*/
public float getMinimumFloat() {
return getMinimumNumber().floatValue();
}
/**
* <p>Gets the maximum number in this range.</p>
*
* @return the maximum number in this range
*/
public abstract Number getMaximumNumber();
/**
* <p>Gets the maximum number in this range as a <code>long</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public long getMaximumLong() {
return getMaximumNumber().longValue();
}
/**
* <p>Gets the maximum number in this range as a <code>int</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public int getMaximumInteger() {
return getMaximumNumber().intValue();
}
/**
* <p>Gets the maximum number in this range as a <code>double</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public double getMaximumDouble() {
return getMaximumNumber().doubleValue();
}
/**
* <p>Gets the maximum number in this range as a <code>float</code>.</p>
*
* <p>This implementation uses the {@link #getMaximumNumber()} method.
* Subclasses may be able to optimise this.</p>
*
* @return the maximum number in this range
*/
public float getMaximumFloat() {
return getMaximumNumber().floatValue();
}
// Include tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* @param number the number to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this range
* @throws IllegalArgumentException if the <code>Number</code> cannot be compared
*/
public abstract boolean containsNumber(Number number);
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>long</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsLong(long)} method.</p>
*
* @param value the long to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>long</code> comparison
*/
public boolean containsLong(Number value) {
if (value == null) {
return false;
}
return containsLong(value.longValue());
}
/**
* <p>Tests whether the specified <code>long</code> occurs within
* this range using <code>long</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumLong()} and
* {@link #getMaximumLong()} methods and should be good for most uses.</p>
*
* @param value the long to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>long</code> comparison
*/
public boolean containsLong(long value) {
return value >= getMinimumLong() && value <= getMaximumLong();
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>int</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsInteger(int)} method.</p>
*
* @param value the integer to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>int</code> comparison
*/
public boolean containsInteger(Number value) {
if (value == null) {
return false;
}
return containsInteger(value.intValue());
}
/**
* <p>Tests whether the specified <code>int</code> occurs within
* this range using <code>int</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumInteger()} and
* {@link #getMaximumInteger()} methods and should be good for most uses.</p>
*
* @param value the int to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>int</code> comparison
*/
public boolean containsInteger(int value) {
return value >= getMinimumInteger() && value <= getMaximumInteger();
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>double</code> comparison..</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsDouble(double)} method.</p>
*
* @param value the double to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>double</code> comparison
*/
public boolean containsDouble(Number value) {
if (value == null) {
return false;
}
return containsDouble(value.doubleValue());
}
/**
* <p>Tests whether the specified <code>double</code> occurs within
* this range using <code>double</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumDouble()} and
* {@link #getMaximumDouble()} methods and should be good for most uses.</p>
*
* @param value the double to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>double</code> comparison
*/
public boolean containsDouble(double value) {
int compareMin = compare(getMinimumDouble(), value);
int compareMax = compare(getMaximumDouble(), value);
return compareMin <= 0 && compareMax >= 0;
}
/**
* <p>Tests whether the specified <code>Number</code> occurs within
* this range using <code>float</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation forwards to the {@link #containsFloat(float)} method.</p>
*
* @param value the float to test, may be <code>null</code>
* @return <code>true</code> if the specified number occurs within this
* range by <code>float</code> comparison
*/
public boolean containsFloat(Number value) {
if (value == null) {
return false;
}
return containsFloat(value.floatValue());
}
/**
* <p>Tests whether the specified <code>float</code> occurs within
* this range using <code>float</code> comparison.</p>
*
* <p>This implementation uses the {@link #getMinimumFloat()} and
* {@link #getMaximumFloat()} methods and should be good for most uses.</p>
*
* @param value the float to test
* @return <code>true</code> if the specified number occurs within this
* range by <code>float</code> comparison
*/
public boolean containsFloat(float value) {
int compareMin = compare(getMinimumFloat(), value);
int compareMax = compare(getMaximumFloat(), value);
return compareMin <= 0 && compareMax >= 0;
}
// Range tests
//--------------------------------------------------------------------
/**
* <p>Tests whether the specified range occurs entirely within this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation uses the {@link #containsNumber(Number)} method.
* Subclasses may be able to optimise this.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range occurs entirely within
* this range; otherwise, <code>false</code>
* @throws IllegalArgumentException if the <code>Range</code> cannot be compared
*/
public boolean containsRange(Range range) {
if (range == null) {
return false;
}
return containsNumber(range.getMinimumNumber())
&& containsNumber(range.getMaximumNumber());
}
/**
* <p>Tests whether the specified range overlaps with this range.</p>
*
* <p>The exact comparison implementation varies by subclass. It is
* intended that an <code>int</code> specific subclass will compare using
* <code>int</code> comparison.</p>
*
* <p><code>null</code> is handled and returns <code>false</code>.</p>
*
* <p>This implementation uses the {@link #containsNumber(Number)} and
* {@link #containsRange(Range)} methods.
* Subclasses may be able to optimise this.</p>
*
* @param range the range to test, may be <code>null</code>
* @return <code>true</code> if the specified range overlaps with this
* range; otherwise, <code>false</code>
* @throws IllegalArgumentException if the <code>Range</code> cannot be compared
*/
public boolean overlapsRange(Range range) {
if (range == null) {
return false;
}
return range.containsNumber(getMinimumNumber())
|| range.containsNumber(getMaximumNumber())
|| containsNumber(range.getMinimumNumber());
}
// Basics
//--------------------------------------------------------------------
/**
* <p>Compares this range to another object to test if they are equal.</p>.
*
* <p>To be equal, the class, minimum and maximum must be equal.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @param obj the reference object with which to compare
* @return <code>true</code> if this object is equal
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj == null || obj.getClass() != getClass()) {
return false;
} else {
Range range = (Range) obj;
return getMinimumNumber().equals(range.getMinimumNumber()) &&
getMaximumNumber().equals(range.getMaximumNumber());
}
}
/**
* <p>Gets a hashCode for the range.</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @return a hash code value for this object
*/
public int hashCode() {
int result = 17;
result = 37 * result + getClass().hashCode();
result = 37 * result + getMinimumNumber().hashCode();
result = 37 * result + getMaximumNumber().hashCode();
return result;
}
/**
* <p>Gets the range as a <code>String</code>.</p>
*
* <p>The format of the String is "Range[<i>min</i>,<i>max</i>]".</p>
*
* <p>This implementation uses the {@link #getMinimumNumber()} and
* {@link #getMaximumNumber()} methods.
* Subclasses may be able to optimise this.</p>
*
* @return the <code>String</code> representation of this range
*/
public String toString() {
StringBuffer buf = new StringBuffer(32);
buf.append("Range[");
buf.append(getMinimumNumber());
buf.append(",");
buf.append(getMaximumNumber());
buf.append("]");
return buf.toString();
}
/**
* <p>Compares two <code>doubles</code> for order.</p>
*
* <p>This method is more comprehensive than the standard Java greater
* than, less than and equals operators.</p>
* <ul>
* <li>It returns <code>-1</code> if the first value is less than the second.</li>
* <li>It returns <code>+1</code> if the first value is greater than the second.</li>
* <li>It returns <code>0</code> if the values are equal.</li>
* </ul>
*
* <p>
* The ordering is as follows, largest to smallest:
* <ul>
* <li>NaN
* <li>Positive infinity
* <li>Maximum double
* <li>Normal positive numbers
* <li>+0.0
* <li>-0.0
* <li>Normal negative numbers
* <li>Minimum double (<code>-Double.MAX_VALUE</code>)
* <li>Negative infinity
* </ul>
* </p>
*
* <p>Comparing <code>NaN</code> with <code>NaN</code> will
* return <code>0</code>.</p>
*
* @param lhs the first <code>double</code>
* @param rhs the second <code>double</code>
* @return <code>-1</code> if lhs is less, <code>+1</code> if greater,
* <code>0</code> if equal to rhs
*/
public static int compare(double lhs, double rhs) {
if (lhs < rhs) {
return -1;
}
if (lhs > rhs) {
return +1;
}
// Need to compare bits to handle 0.0 == -0.0 being true
// compare should put -0.0 < +0.0
// Two NaNs are also == for compare purposes
// where NaN == NaN is false
long lhsBits = Double.doubleToLongBits(lhs);
long rhsBits = Double.doubleToLongBits(rhs);
if (lhsBits == rhsBits) {
return 0;
}
// Something exotic! A comparison to NaN or 0.0 vs -0.0
// Fortunately NaN"s long is > than everything else
// Also negzeros bits < poszero
// NAN: 9221120237041090560
// MAX: 9218868437227405311
// NEGZERO: -9223372036854775808
if (lhsBits < rhsBits) {
return -1;
} else {
return +1;
}
}
/**
* <p>Compares two floats for order.</p>
*
* <p>This method is more comprehensive than the standard Java greater than,
* less than and equals operators.</p>
* <ul>
* <li>It returns <code>-1</code> if the first value is less than the second.
* <li>It returns <code>+1</code> if the first value is greater than the second.
* <li>It returns <code>0</code> if the values are equal.
* </ul>
*
* <p> The ordering is as follows, largest to smallest:
* <ul>
* <li>NaN
* <li>Positive infinity
* <li>Maximum float
* <li>Normal positive numbers
* <li>+0.0
* <li>-0.0
* <li>Normal negative numbers
* <li>Minimum float (<code>-Float.MAX_VALUE</code>)
* <li>Negative infinity
* </ul>
*
* <p>Comparing <code>NaN</code> with <code>NaN</code> will return
* <code>0</code>.</p>
*
* @param lhs the first <code>float</code>
* @param rhs the second <code>float</code>
* @return <code>-1</code> if lhs is less, <code>+1</code> if greater,
* <code>0</code> if equal to rhs
*/
public static int compare(float lhs, float rhs) {
if (lhs < rhs) {
return -1;
}
if (lhs > rhs) {
return +1;
}
//Need to compare bits to handle 0.0 == -0.0 being true
// compare should put -0.0 < +0.0
// Two NaNs are also == for compare purposes
// where NaN == NaN is false
int lhsBits = Float.floatToIntBits(lhs);
int rhsBits = Float.floatToIntBits(rhs);
if (lhsBits == rhsBits) {
return 0;
}
//Something exotic! A comparison to NaN or 0.0 vs -0.0
//Fortunately NaN"s int is > than everything else
//Also negzeros bits < poszero
//NAN: 2143289344
//MAX: 2139095039
//NEGZERO: -2147483648
if (lhsBits < rhsBits) {
return -1;
} else {
return +1;
}
}
}
Represents a range of Number objects.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* <p>Represents a range of {@link Number} objects.</p>
*
* <p>This class uses <code>double</code> comparisons. This means that it
* is unsuitable for dealing with large <code>Long</code>, <code>BigDecimal</code>
* or <code>BigInteger</code> numbers.</p>
*
* @author
* @author Stephen Colebourne
* @since 1.0
* @version $Revision: 437554 $ $Date: 2006-08-27 23:21:41 -0700 (Sun, 27 Aug 2006) $
*
*
*/
public final class NumberRange {
/* The minimum number in this range. */
private final Number min;
/* The maximum number in this range. */
private final Number max;
/**
* <p>Constructs a new <code>NumberRange</code> using
* <code>number</code> as both the minimum and maximum in
* this range.</p>
*
* @param num the number to use for this range
* @throws NullPointerException if the number is <code>null</code>
*/
public NumberRange(Number num) {
if (num == null) {
throw new NullPointerException("The number must not be null");
}
this.min = num;
this.max = num;
}
/**
* <p>Constructs a new <code>NumberRange</code> with the specified
* minimum and maximum numbers.</p>
*
* <p><em>If the maximum is less than the minimum, the range will be constructed
* from the minimum value to the minimum value, not what you would expect!.</em></p>
*
* @param min the minimum number in this range
* @param max the maximum number in this range
* @throws NullPointerException if either the minimum or maximum number is
* <code>null</code>
*/
public NumberRange(Number min, Number max) {
if (min == null) {
throw new NullPointerException("The minimum value must not be null");
} else if (max == null) {
throw new NullPointerException("The maximum value must not be null");
}
if (max.doubleValue() < min.doubleValue()) {
this.min = this.max = min;
} else {
this.min = min;
this.max = max;
}
}
/**
* <p>Returns the minimum number in this range.</p>
*
* @return the minimum number in this range
*/
public Number getMinimum() {
return min;
}
/**
* <p>Returns the maximum number in this range.</p>
*
* @return the maximum number in this range
*/
public Number getMaximum() {
return max;
}
/**
* <p>Tests whether the specified <code>number</code> occurs within
* this range using <code>double</code> comparison.</p>
*
* @param number the number to test
* @return <code>true</code> if the specified number occurs within this
* range; otherwise, <code>false</code>
*/
public boolean includesNumber(Number number) {
if (number == null) {
return false;
} else {
return !(min.doubleValue() > number.doubleValue()) &&
!(max.doubleValue() < number.doubleValue());
}
}
/**
* <p>Tests whether the specified range occurs entirely within this
* range using <code>double</code> comparison.</p>
*
* @param range the range to test
* @return <code>true</code> if the specified range occurs entirely within
* this range; otherwise, <code>false</code>
*/
public boolean includesRange(NumberRange range) {
if (range == null) {
return false;
} else {
return includesNumber(range.min) && includesNumber(range.max);
}
}
/**
* <p>Tests whether the specified range overlaps with this range
* using <code>double</code> comparison.</p>
*
* @param range the range to test
* @return <code>true</code> if the specified range overlaps with this
* range; otherwise, <code>false</code>
*/
public boolean overlaps(NumberRange range) {
if (range == null) {
return false;
} else {
return range.includesNumber(min) || range.includesNumber(max) ||
includesRange(range);
}
}
/**
* <p>Indicates whether some other <code>Object</code> is
* "equal" to this one.</p>
*
* @param obj the reference object with which to compare
* @return <code>true</code> if this object is the same as the obj
* argument; <code>false</code> otherwise
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (!(obj instanceof NumberRange)) {
return false;
} else {
NumberRange range = (NumberRange)obj;
return min.equals(range.min) && max.equals(range.max);
}
}
/**
* <p>Returns a hash code value for this object.</p>
*
* @return a hash code value for this object
*/
public int hashCode() {
int result = 17;
result = 37 * result + min.hashCode();
result = 37 * result + max.hashCode();
return result;
}
/**
* <p>Returns the string representation of this range.</p>
*
* <p>This string is the string representation of the minimum and
* maximum numbers in the range, separated by a hyphen. If a number
* is negative, then it is enclosed in parentheses.</p>
*
* @return the string representation of this range
*/
public String toString() {
StringBuffer sb = new StringBuffer();
if (min.doubleValue() < 0) {
sb.append("(")
.append(min)
.append(")");
} else {
sb.append(min);
}
sb.append("-");
if (max.doubleValue() < 0) {
sb.append("(")
.append(max)
.append(")");
} else {
sb.append(max);
}
return sb.toString();
}
}
Represents a sequence of integer values, either ascending or descending.
// Copyright 2006 The Apache Software Foundation
//
// 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.
import java.util.Iterator;
/**
* Represents a sequence of integer values, either ascending or descending. The sequence is always inclusive (of the
* finish value).
*/
public final class IntegerRange implements Iterable<Integer>
{
private final int start;
private final int finish;
private class RangeIterator implements Iterator<Integer>
{
private final int increment;
private int value = start;
private boolean hasNext = true;
RangeIterator()
{
increment = start < finish ? +1 : -1;
}
public boolean hasNext()
{
return hasNext;
}
public Integer next()
{
if (!hasNext) throw new IllegalStateException();
int result = value;
hasNext = value != finish;
value += increment;
return result;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
public IntegerRange(final int start, final int finish)
{
this.start = start;
this.finish = finish;
}
public int getFinish()
{
return finish;
}
public int getStart()
{
return start;
}
@Override
public String toString()
{
return String.format("%d..%d", start, finish);
}
/**
* The main puprose of a range object is to produce an Iterator. Since IntegerRange is iterable, it is useful with
* the Tapestry Loop component, but also with the Java for loop!
*/
public Iterator<Integer> iterator()
{
return new RangeIterator();
}
@Override
public int hashCode()
{
final int PRIME = 31;
int result = PRIME + finish;
result = PRIME * result + start;
return result;
}
/**
* Returns true if the other object is an IntegerRange with the same start and finish values.
*/
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final IntegerRange other = (IntegerRange) obj;
if (finish != other.finish) return false;
return start == other.start;
}
}
This constructs an Iterator over each day in a date range defined by a focus date and range style.
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.TimeZone;
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* <p>A suite of utilities surrounding the use of the
* {@link java.util.Calendar} and {@link java.util.Date} object.</p>
*
* <p>DateUtils contains a lot of common methods considering manipulations
* of Dates or Calendars. Some methods require some extra explanation.
* The truncate and round methods could be considered the Math.floor(),
* Math.ceil() or Math.round versions for dates
* This way date-fields will be ignored in bottom-up order.
* As a complement to these methods we"ve introduced some fragment-methods.
* With these methods the Date-fields will be ignored in top-down order.
* Since a date without a year is not a valid date, you have to decide in what
* kind of date-field you want your result, for instance milliseconds or days.
* </p>
*
*
*
* @author
* @author Phil Steitz
* @author Robert Scholte
* @since 2.0
* @version $Id: DateUtils.java 634096 2008-03-06 00:58:11Z niallp $
*/
public class Main {
/**
* The UTC time zone (often referred to as GMT).
*/
public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("GMT");
/**
* Number of milliseconds in a standard second.
* @since 2.1
*/
public static final long MILLIS_PER_SECOND = 1000;
/**
* Number of milliseconds in a standard minute.
* @since 2.1
*/
public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
/**
* Number of milliseconds in a standard hour.
* @since 2.1
*/
public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
/**
* Number of milliseconds in a standard day.
* @since 2.1
*/
public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
/**
* This is half a month, so this represents whether a date is in the top
* or bottom half of the month.
*/
public final static int SEMI_MONTH = 1001;
private static final int[][] fields = {
{Calendar.MILLISECOND},
{Calendar.SECOND},
{Calendar.MINUTE},
{Calendar.HOUR_OF_DAY, Calendar.HOUR},
{Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM
/* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
},
{Calendar.MONTH, DateUtils.SEMI_MONTH},
{Calendar.YEAR},
{Calendar.ERA}};
/**
* A week range, starting on Sunday.
*/
public final static int RANGE_WEEK_SUNDAY = 1;
/**
* A week range, starting on Monday.
*/
public final static int RANGE_WEEK_MONDAY = 2;
/**
* A week range, starting on the day focused.
*/
public final static int RANGE_WEEK_RELATIVE = 3;
/**
* A week range, centered around the day focused.
*/
public final static int RANGE_WEEK_CENTER = 4;
/**
* A month range, the week starting on Sunday.
*/
public final static int RANGE_MONTH_SUNDAY = 5;
/**
* A month range, the week starting on Monday.
*/
public final static int RANGE_MONTH_MONDAY = 6;
//-----------------------------------------------------------------------
/**
* <p>This constructs an <code>Iterator</code> over each day in a date
* range defined by a focus date and range style.</p>
*
* <p>For instance, passing Thursday, July 4, 2002 and a
* <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
* that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
* 2002, returning a Calendar instance for each intermediate day.</p>
*
* <p>This method provides an iterator that returns Calendar objects.
* The days are progressed using {@link Calendar#add(int, int)}.</p>
*
* @param focus the date to work with, not null
* @param rangeStyle the style constant to use. Must be one of
* {@link DateUtils#RANGE_MONTH_SUNDAY},
* {@link DateUtils#RANGE_MONTH_MONDAY},
* {@link DateUtils#RANGE_WEEK_SUNDAY},
* {@link DateUtils#RANGE_WEEK_MONDAY},
* {@link DateUtils#RANGE_WEEK_RELATIVE},
* {@link DateUtils#RANGE_WEEK_CENTER}
* @return the date iterator, which always returns Calendar instances
* @throws IllegalArgumentException if the date is <code>null</code>
* @throws IllegalArgumentException if the rangeStyle is invalid
*/
public static Iterator iterator(Date focus, int rangeStyle) {
if (focus == null) {
throw new IllegalArgumentException("The date must not be null");
}
Calendar gval = Calendar.getInstance();
gval.setTime(focus);
return iterator(gval, rangeStyle);
}
/**
* <p>This constructs an <code>Iterator</code> over each day in a date
* range defined by a focus date and range style.</p>
*
* <p>For instance, passing Thursday, July 4, 2002 and a
* <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
* that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
* 2002, returning a Calendar instance for each intermediate day.</p>
*
* <p>This method provides an iterator that returns Calendar objects.
* The days are progressed using {@link Calendar#add(int, int)}.</p>
*
* @param focus the date to work with
* @param rangeStyle the style constant to use. Must be one of
* {@link DateUtils#RANGE_MONTH_SUNDAY},
* {@link DateUtils#RANGE_MONTH_MONDAY},
* {@link DateUtils#RANGE_WEEK_SUNDAY},
* {@link DateUtils#RANGE_WEEK_MONDAY},
* {@link DateUtils#RANGE_WEEK_RELATIVE},
* {@link DateUtils#RANGE_WEEK_CENTER}
* @return the date iterator
* @throws IllegalArgumentException if the date is <code>null</code>
* @throws IllegalArgumentException if the rangeStyle is invalid
*/
public static Iterator iterator(Calendar focus, int rangeStyle) {
if (focus == null) {
throw new IllegalArgumentException("The date must not be null");
}
Calendar start = null;
Calendar end = null;
int startCutoff = Calendar.SUNDAY;
int endCutoff = Calendar.SATURDAY;
switch (rangeStyle) {
case RANGE_MONTH_SUNDAY:
case RANGE_MONTH_MONDAY:
//Set start to the first of the month
start = truncate(focus, Calendar.MONTH);
//Set end to the last of the month
end = (Calendar) start.clone();
end.add(Calendar.MONTH, 1);
end.add(Calendar.DATE, -1);
//Loop start back to the previous sunday or monday
if (rangeStyle == RANGE_MONTH_MONDAY) {
startCutoff = Calendar.MONDAY;
endCutoff = Calendar.SUNDAY;
}
break;
case RANGE_WEEK_SUNDAY:
case RANGE_WEEK_MONDAY:
case RANGE_WEEK_RELATIVE:
case RANGE_WEEK_CENTER:
//Set start and end to the current date
start = truncate(focus, Calendar.DATE);
end = truncate(focus, Calendar.DATE);
switch (rangeStyle) {
case RANGE_WEEK_SUNDAY:
//already set by default
break;
case RANGE_WEEK_MONDAY:
startCutoff = Calendar.MONDAY;
endCutoff = Calendar.SUNDAY;
break;
case RANGE_WEEK_RELATIVE:
startCutoff = focus.get(Calendar.DAY_OF_WEEK);
endCutoff = startCutoff - 1;
break;
case RANGE_WEEK_CENTER:
startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3;
endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3;
break;
}
break;
default:
throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid.");
}
if (startCutoff < Calendar.SUNDAY) {
startCutoff += 7;
}
if (startCutoff > Calendar.SATURDAY) {
startCutoff -= 7;
}
if (endCutoff < Calendar.SUNDAY) {
endCutoff += 7;
}
if (endCutoff > Calendar.SATURDAY) {
endCutoff -= 7;
}
while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) {
start.add(Calendar.DATE, -1);
}
while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) {
end.add(Calendar.DATE, 1);
}
return new DateIterator(start, end);
}
/**
* <p>This constructs an <code>Iterator</code> over each day in a date
* range defined by a focus date and range style.</p>
*
* <p>For instance, passing Thursday, July 4, 2002 and a
* <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
* that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
* 2002, returning a Calendar instance for each intermediate day.</p>
*
* @param focus the date to work with, either
* <code>Date</code> or <code>Calendar</code>
* @param rangeStyle the style constant to use. Must be one of the range
* styles listed for the {@link #iterator(Calendar, int)} method.
* @return the date iterator
* @throws IllegalArgumentException if the date
* is <code>null</code>
* @throws ClassCastException if the object type is
* not a <code>Date</code> or <code>Calendar</code>
*/
public static Iterator iterator(Object focus, int rangeStyle) {
if (focus == null) {
throw new IllegalArgumentException("The date must not be null");
}
if (focus instanceof Date) {
return iterator((Date) focus, rangeStyle);
} else if (focus instanceof Calendar) {
return iterator((Calendar) focus, rangeStyle);
} else {
throw new ClassCastException("Could not iterate based on " + focus);
}
}
/**
* <p>Truncate this date, leaving the field specified as the most
* significant field.</p>
*
* <p>For example, if you had the datetime of 28 Mar 2002
* 13:45:01.231, if you passed with HOUR, it would return 28 Mar
* 2002 13:00:00.000. If this was passed with MONTH, it would
* return 1 Mar 2002 0:00:00.000.</p>
*
* @param date the date to work with, either <code>Date</code>
* or <code>Calendar</code>
* @param field the field from <code>Calendar</code>
* or <code>SEMI_MONTH</code>
* @return the rounded date
* @throws IllegalArgumentException if the date
* is <code>null</code>
* @throws ClassCastException if the object type is not a
* <code>Date</code> or <code>Calendar</code>
* @throws ArithmeticException if the year is over 280 million
*/
public static Date truncate(Object date, int field) {
if (date == null) {
throw new IllegalArgumentException("The date must not be null");
}
if (date instanceof Date) {
return truncate((Date) date, field);
} else if (date instanceof Calendar) {
return truncate((Calendar) date, field).getTime();
} else {
throw new ClassCastException("Could not truncate " + date);
}
}
/**
* <p>Truncate this date, leaving the field specified as the most
* significant field.</p>
*
* <p>For example, if you had the datetime of 28 Mar 2002
* 13:45:01.231, if you passed with HOUR, it would return 28 Mar
* 2002 13:00:00.000. If this was passed with MONTH, it would
* return 1 Mar 2002 0:00:00.000.</p>
*
* @param date the date to work with
* @param field the field from <code>Calendar</code>
* or <code>SEMI_MONTH</code>
* @return the rounded date (a different object)
* @throws IllegalArgumentException if the date is <code>null</code>
* @throws ArithmeticException if the year is over 280 million
*/
public static Calendar truncate(Calendar date, int field) {
if (date == null) {
throw new IllegalArgumentException("The date must not be null");
}
Calendar truncated = (Calendar) date.clone();
modify(truncated, field, false);
return truncated;
}
//-----------------------------------------------------------------------
/**
* <p>Internal calculation method.</p>
*
* @param val the calendar
* @param field the field constant
* @param round true to round, false to truncate
* @throws ArithmeticException if the year is over 280 million
*/
private static void modify(Calendar val, int field, boolean round) {
if (val.get(Calendar.YEAR) > 280000000) {
throw new ArithmeticException("Calendar value too large for accurate calculations");
}
if (field == Calendar.MILLISECOND) {
return;
}
// ----------------- Fix for LANG-59 ---------------------- START ---------------
// see http://issues.apache.org/jira/browse/LANG-59
//
// Manually truncate milliseconds, seconds and minutes, rather than using
// Calendar methods.
Date date = val.getTime();
long time = date.getTime();
boolean done = false;
// truncate milliseconds
int millisecs = val.get(Calendar.MILLISECOND);
if (!round || millisecs < 500) {
time = time - millisecs;
}
if (field == Calendar.SECOND) {
done = true;
}
// truncate seconds
int seconds = val.get(Calendar.SECOND);
if (!done && (!round || seconds < 30)) {
time = time - (seconds * 1000L);
}
if (field == Calendar.MINUTE) {
done = true;
}
// truncate minutes
int minutes = val.get(Calendar.MINUTE);
if (!done && (!round || minutes < 30)) {
time = time - (minutes * 60000L);
}
// reset time
if (date.getTime() != time) {
date.setTime(time);
val.setTime(date);
}
// ----------------- Fix for LANG-59 ----------------------- END ----------------
boolean roundUp = false;
for (int i = 0; i < fields.length; i++) {
for (int j = 0; j < fields[i].length; j++) {
if (fields[i][j] == field) {
//This is our field... we stop looping
if (round && roundUp) {
if (field == DateUtils.SEMI_MONTH) {
//This is a special case that"s hard to generalize
//If the date is 1, we round up to 16, otherwise
// we subtract 15 days and add 1 month
if (val.get(Calendar.DATE) == 1) {
val.add(Calendar.DATE, 15);
} else {
val.add(Calendar.DATE, -15);
val.add(Calendar.MONTH, 1);
}
} else {
//We need at add one to this field since the
// last number causes us to round up
val.add(fields[i][0], 1);
}
}
return;
}
}
//We have various fields that are not easy roundings
int offset = 0;
boolean offsetSet = false;
//These are special types of fields that require different rounding rules
switch (field) {
case DateUtils.SEMI_MONTH:
if (fields[i][0] == Calendar.DATE) {
//If we"re going to drop the DATE field"s value,
// we want to do this our own way.
//We need to subtrace 1 since the date has a minimum of 1
offset = val.get(Calendar.DATE) - 1;
//If we"re above 15 days adjustment, that means we"re in the
// bottom half of the month and should stay accordingly.
if (offset >= 15) {
offset -= 15;
}
//Record whether we"re in the top or bottom half of that range
roundUp = offset > 7;
offsetSet = true;
}
break;
case Calendar.AM_PM:
if (fields[i][0] == Calendar.HOUR_OF_DAY) {
//If we"re going to drop the HOUR field"s value,
// we want to do this our own way.
offset = val.get(Calendar.HOUR_OF_DAY);
if (offset >= 12) {
offset -= 12;
}
roundUp = offset > 6;
offsetSet = true;
}
break;
}
if (!offsetSet) {
int min = val.getActualMinimum(fields[i][0]);
int max = val.getActualMaximum(fields[i][0]);
//Calculate the offset from the minimum allowed value
offset = val.get(fields[i][0]) - min;
//Set roundUp if this is more than half way between the minimum and maximum
roundUp = offset > ((max - min) / 2);
}
//We need to remove this field
if (offset != 0) {
val.set(fields[i][0], val.get(fields[i][0]) - offset);
}
}
throw new IllegalArgumentException("The field " + field + " is not supported");
}
/**
* <p>Date iterator.</p>
*/
static class DateIterator implements Iterator {
private final Calendar endFinal;
private final Calendar spot;
/**
* Constructs a DateIterator that ranges from one date to another.
*
* @param startFinal start date (inclusive)
* @param endFinal end date (not inclusive)
*/
DateIterator(Calendar startFinal, Calendar endFinal) {
super();
this.endFinal = endFinal;
spot = startFinal;
spot.add(Calendar.DATE, -1);
}
/**
* Has the iterator not reached the end date yet?
*
* @return <code>true</code> if the iterator has yet to reach the end date
*/
public boolean hasNext() {
return spot.before(endFinal);
}
/**
* Return the next calendar in the iteration
*
* @return Object calendar for the next date
*/
public Object next() {
if (spot.equals(endFinal)) {
throw new RuntimeException();
}
spot.add(Calendar.DATE, 1);
return spot.clone();
}
/**
* Always throws UnsupportedOperationException.
*
* @throws UnsupportedOperationException
* @see java.util.Iterator#remove()
*/
public void remove() {
throw new UnsupportedOperationException();
}
}
}