You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ba...@apache.org on 2009/11/06 08:56:22 UTC

svn commit: r833308 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/Range.java test/org/apache/commons/lang/RangeTest.java

Author: bayard
Date: Fri Nov  6 07:56:22 2009
New Revision: 833308

URL: http://svn.apache.org/viewvc?rev=833308&view=rev
Log:
Committing work in progress version of a new Range class to replace the math.*Range classes as discussed in LANG-551

Added:
    commons/proper/lang/trunk/src/java/org/apache/commons/lang/Range.java   (with props)
    commons/proper/lang/trunk/src/test/org/apache/commons/lang/RangeTest.java   (with props)

Added: commons/proper/lang/trunk/src/java/org/apache/commons/lang/Range.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/apache/commons/lang/Range.java?rev=833308&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/java/org/apache/commons/lang/Range.java (added)
+++ commons/proper/lang/trunk/src/java/org/apache/commons/lang/Range.java Fri Nov  6 07:56:22 2009
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+package org.apache.commons.lang;
+
+import java.util.Comparator;
+
+/**
+ * <p><code>Range</code> represents a range of numbers of the same type.</p>
+ * 
+ * @author Apache Software Foundation
+ * @since 3.0
+ * @version $Id: Range.java 830032 2009-10-27 00:15:00Z scolebourne $
+ */
+// TODO: Serializable?
+public class Range<T> {
+
+    private final Comparator comparator;
+    private final T minimum;
+    private final T maximum;
+
+    /**
+     * <p>Constructs a new <code>Range</code> using the specified
+     * element as both the minimum and maximum in this range.</p>
+     * <p>The range uses the natural ordering of the elements to 
+     * determine where values lie in the range.</p>
+     *
+     * @param element  the value to use for this range, must not be <code>null</code>
+     * @throws IllegalArgumentException if the value is <code>null</code>
+     */
+// TODO: Ideally this would only support <T extends Comparable<? super T>>
+    public Range(T element) {
+        this(element, element);
+    }
+
+    /**
+     * <p>Constructs a new <code>Range</code> with the specified
+     * minimum and maximum values (both inclusive).</p>
+     * <p>The range uses the natural ordering of the elements to 
+     * determine where values lie in the range.</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 element1  first value that defines the edge of the range, inclusive
+     * @param element2  second value that defines the edge of the range, inclusive
+     * @throws IllegalArgumentException if either value is <code>null</code>
+     */
+// TODO: Ideally this would only support <T extends Comparable<? super T>>
+    public Range(T element1, T element2) {
+        this(element1, element2, ComparableComparator.INSTANCE);
+    }
+
+    /**
+     * <p>Constructs a new <code>Range</code> using the specified
+     * element as both the minimum and maximum in this range.</p>
+     * <p>The range uses the passed in <code>Comparator</code> to 
+     * determine where values lie in the range.</p>
+     *
+     * @param element  the value to use for this range, must not be <code>null</code>
+     * @throws IllegalArgumentException if the value is <code>null</code>
+     */
+    public Range(T element, Comparator c) {
+        this(element, element, c);
+    }
+
+    /**
+     * <p>Constructs a new <code>Range</code> with the specified
+     * minimum and maximum values (both inclusive).</p>
+     * <p>The range uses the passed in <code>Comparator</code> to 
+     * determine where values lie in the range.</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 element1  first value that defines the edge of the range, inclusive
+     * @param element2  second value that defines the edge of the range, inclusive
+     * @throws IllegalArgumentException if either value is <code>null</code>
+     */
+    public Range(T element1, T element2, Comparator c) {
+        if(element1 == null || element2 == null) {
+            throw new IllegalArgumentException("Elements in a range must not be null: element1=" + 
+                                               element1 + ", element2=" + element2);
+        }
+
+        if(c == null) {
+            throw new IllegalArgumentException("Comparator must not be null");
+        }
+
+        if(c.compare(element1, element2) < 1) {
+            this.minimum = element1;
+            this.maximum = element2;
+        } else {
+            this.minimum = element2;
+            this.maximum = element1;
+        }
+        this.comparator = c;
+    }
+
+    // Accessors
+    //--------------------------------------------------------------------
+
+    /**
+     * <p>Gets the minimum value in this range.</p>
+     *
+     * @return the minimum value in this range
+     */
+    public T getMinimum() {
+        return this.minimum;
+    }
+
+    /**
+     * <p>Gets the maximum value in this range.</p>
+     *
+     * @return the maximum value in this range
+     */
+    public T getMaximum() {
+        return this.maximum;
+    }
+
+    /**
+     * <p>Gets the comparator being used to determine if objects are within the range. </p>
+     *
+     * @return the comparator being used
+     */
+    public Comparator getComparator() {
+        return this.comparator;
+    }
+
+    /**
+     * <p>Whether or not the Range is using the default natural comparison method 
+     * to compare elements. </p>
+     *
+     * @return whether or not the default Comparator is in use
+     */
+    public boolean isDefaultNaturalOrdering() {
+        return this.comparator == ComparableComparator.INSTANCE;
+    }
+
+    // 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 boolean contains(T t) {
+// TODO: Rewrite in terms of !lessThan and !greaterThan?
+        return (comparator.compare(t, getMinimum()) > -1) && (comparator.compare(t, getMaximum()) < 1);
+    }
+
+    public boolean lessThan(T element) {
+        if (element == null) {
+            return false;
+        }
+        
+        return this.comparator.compare(this.getMinimum(), element) < 1;
+    }
+
+    public boolean greaterThan(T element) {
+        if (element == null) {
+            return false;
+        }
+        
+        return this.comparator.compare(getMaximum(), element) > -1;
+    }
+
+    // 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<T> range) {
+        if (range == null) {
+            return false;
+        }
+        return contains(range.getMinimum()) 
+            && contains(range.getMaximum());
+    }
+
+    /**
+     * <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<T> range) {
+        if (range == null) {
+            return false;
+        }
+        return range.contains(getMinimum())
+            || range.contains(getMaximum())
+            || contains(range.getMinimum());
+    }
+
+    // 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
+     */
+    @Override
+    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 getMinimum().equals(range.getMinimum()) &&
+                   getMaximum().equals(range.getMaximum());
+        }
+    }
+
+    /**
+     * <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
+     */
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 37 * result + getClass().hashCode();
+        result = 37 * result + getMinimum().hashCode();
+        result = 37 * result + getMaximum().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
+     */
+    @Override
+    public String toString() {
+        StringBuilder buf = new StringBuilder(32);
+        buf.append("Range[");
+        buf.append(getMinimum());
+        buf.append(',');
+        buf.append(getMaximum());
+        buf.append(']');
+        return buf.toString();
+    }
+
+
+    // Taken from Commons Collections - documentation removed as not a public class
+    private static class ComparableComparator<E extends Comparable<? super E>> implements Comparator<E> {
+
+        @SuppressWarnings("unchecked")
+        public static final ComparableComparator<?> INSTANCE = new ComparableComparator();
+
+        @SuppressWarnings("unchecked")
+        public static <E extends Comparable<? super E>> ComparableComparator<E> getInstance() {
+            return (ComparableComparator<E>) INSTANCE;
+        }
+
+        public ComparableComparator() {
+            super();
+        }
+
+        public int compare(E obj1, E obj2) {
+            return obj1.compareTo(obj2);
+        }
+
+        public int hashCode() {
+            return "ComparableComparator".hashCode();
+        }
+
+        public boolean equals(Object object) {
+            return (this == object) || 
+                   ((null != object) && (object.getClass().equals(this.getClass())));
+        }
+
+    }
+
+}

Propchange: commons/proper/lang/trunk/src/java/org/apache/commons/lang/Range.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/lang/trunk/src/test/org/apache/commons/lang/RangeTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/org/apache/commons/lang/RangeTest.java?rev=833308&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/org/apache/commons/lang/RangeTest.java (added)
+++ commons/proper/lang/trunk/src/test/org/apache/commons/lang/RangeTest.java Fri Nov  6 07:56:22 2009
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.lang;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * <p>
+ * Tests the methods in the {@link org.apache.commons.lang.Range} class.
+ * </p>
+ * 
+ * @version $Id: RangeTest.java 754804 2009-03-16 02:06:18Z sebb $
+ */
+public class RangeTest extends TestCase {
+
+    private Range byteRange;
+    private Range byteRange2;
+    private Range byteRange3;
+
+    private Range<Integer> intRange;
+    private Range<Long> longRange;
+    private Range<Float> floatRange;
+    private Range<Double> doubleRange;
+
+    @Override
+    public void setUp() {
+        byteRange   = new Range((byte) 0, (byte) 5);
+        byteRange2  = new Range((byte) 0, (byte) 5);
+        byteRange3  = new Range((byte) 0, (byte) 10);
+
+        intRange    = new Range<Integer>((int) 10, (int) 20);
+        longRange   = new Range<Long>((long) 10, (long) 20);
+        floatRange  = new Range<Float>((float) 10, (float) 20);
+        doubleRange = new Range<Double>((double) 10, (double) 20);
+    }
+
+    /**
+     * Test method for 'org.apache.commons.lang.Range.equals(Object)'
+     */
+    public void testEqualsObject() {
+        assertEquals(byteRange, byteRange);
+        assertEquals(byteRange, byteRange2);
+        assertEquals(byteRange2, byteRange2);
+        assertTrue(byteRange.equals(byteRange));
+        assertTrue(byteRange2.equals(byteRange2));
+        assertTrue(byteRange3.equals(byteRange3));
+        assertFalse(byteRange2.equals(byteRange3));
+        assertFalse(byteRange2.equals(null));
+        assertFalse(byteRange2.equals("Ni!"));
+    }
+
+    /**
+     * Test method for 'org.apache.commons.lang.Range.hashCode()'
+     */
+    public void testHashCode() {
+        assertEquals(byteRange.hashCode(), byteRange2.hashCode());
+        assertFalse(byteRange.hashCode() == byteRange3.hashCode());
+    }
+
+    /**
+     * Test method for 'org.apache.commons.lang.Range.toString()'
+     */
+    public void testToString() {
+        assertNotNull(byteRange.toString());
+    }
+
+    // --------------------------------------------------------------------------
+    public void testGetMinimum() {
+        assertEquals(10, (int) intRange.getMinimum());
+        assertEquals(10L, (long) longRange.getMinimum());
+        assertEquals(10f, floatRange.getMinimum(), 0.00001f);
+        assertEquals(10d, doubleRange.getMinimum(), 0.00001d);
+    }
+    
+    public void testGetMaximum() {
+        assertEquals(20, (int) intRange.getMaximum());
+        assertEquals(20L, (long) longRange.getMaximum());
+        assertEquals(20f, floatRange.getMaximum(), 0.00001f);
+        assertEquals(20d, doubleRange.getMaximum(), 0.00001d);
+    }
+}

Propchange: commons/proper/lang/trunk/src/test/org/apache/commons/lang/RangeTest.java
------------------------------------------------------------------------------
    svn:eol-style = native