Browse Source

DATACMNS-651 - Introduced a Range value type.

Primarily intended to be used with Distance instances, we introduce a Range value type. Distance now has factory methods to create Range instances between two distances. To support this, Distance now implements comparable based on the normalized value of it.

Tiny refactoring in TypeDiscoverer to avoid code duplication between the lookup of parameter type information for constructors and methods.
pull/117/head
Oliver Gierke 11 years ago
parent
commit
2d75cf2c43
  1. 96
      src/main/java/org/springframework/data/domain/Range.java
  2. 39
      src/main/java/org/springframework/data/geo/Distance.java
  3. 35
      src/main/java/org/springframework/data/util/TypeDiscoverer.java
  4. 113
      src/test/java/org/springframework/data/domain/RangeUnitTests.java
  5. 54
      src/test/java/org/springframework/data/geo/DistanceUnitTests.java

96
src/main/java/org/springframework/data/domain/Range.java

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import org.springframework.util.Assert;
/**
* Simple value object to work with ranges.
*
* @author Oliver Gierke
* @since 1.10
*/
public class Range<T extends Comparable<T>> {
private final T lowerBound;
private final T upperBound;
private final boolean lowerInclusive;
private final boolean upperInclusive;
/**
* Creates a new {@link Range} with the given lower and upper bound. Treats the given values as inclusive bounds. Use
* {@link #Range(Comparable, Comparable, boolean, boolean)} to configure different bound behavior.
*
* @see #Range(Comparable, Comparable, boolean, boolean)
* @param lowerBound can be {@literal null} in case upperBound is not {@literal null}.
* @param upperBound can be {@literal null} in case lowerBound is not {@literal null}.
*/
public Range(T lowerBound, T upperBound) {
this(lowerBound, upperBound, true, true);
}
/**
* Createsa new {@link Range} with the given lower and upper bound as well as the given inclusive/exclusive semantics.
*
* @param lowerBound can be {@literal null}.
* @param upperBound can be {@literal null}.
* @param lowerInclusive
* @param upperInclusive
*/
public Range(T lowerBound, T upperBound, boolean lowerInclusive, boolean upperInclusive) {
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.lowerInclusive = lowerInclusive;
this.upperInclusive = upperInclusive;
}
/**
* Returns the lower bound of the range.
*
* @return can be {@literal null}.
*/
public T getLowerBound() {
return lowerBound;
}
/**
* Returns the upper bound of the range.
*
* @return can be {@literal null}.
*/
public T getUpperBound() {
return upperBound;
}
/**
* Returns whether the {@link Range} contains the given value.
*
* @param value must not be {@literal null}.
* @return
*/
public boolean contains(T value) {
Assert.notNull(value, "Reference value must not be null!");
boolean greaterThanLowerBound = lowerBound == null ? true : lowerInclusive ? lowerBound.compareTo(value) <= 0
: lowerBound.compareTo(value) < 0;
boolean lessThanUpperBound = upperBound == null ? true : upperInclusive ? upperBound.compareTo(value) >= 0
: upperBound.compareTo(value) > 0;
return greaterThanLowerBound && lessThanUpperBound;
}
}

39
src/main/java/org/springframework/data/geo/Distance.java

@ -17,6 +17,7 @@ package org.springframework.data.geo; @@ -17,6 +17,7 @@ package org.springframework.data.geo;
import java.io.Serializable;
import org.springframework.data.domain.Range;
import org.springframework.util.Assert;
/**
@ -26,7 +27,7 @@ import org.springframework.util.Assert; @@ -26,7 +27,7 @@ import org.springframework.util.Assert;
* @author Thomas Darimont
* @since 1.8
*/
public class Distance implements Serializable {
public class Distance implements Serializable, Comparable<Distance> {
private static final long serialVersionUID = 2460886201934027744L;
@ -54,6 +55,30 @@ public class Distance implements Serializable { @@ -54,6 +55,30 @@ public class Distance implements Serializable {
this.metric = metric == null ? Metrics.NEUTRAL : metric;
}
/**
* Creates a {@link Range} between the given {@link Distance}.
*
* @param min can be {@literal null}.
* @param max can be {@literal null}.
* @return will never be {@literal null}.
*/
public static Range<Distance> between(Distance min, Distance max) {
return new Range<Distance>(min, max);
}
/**
* Creates a new {@link Range} by creating minimum and maximum {@link Distance} from the given values.
*
* @param minValue
* @param minMetric can be {@literal null}.
* @param maxValue
* @param maxMetric can be {@literal null}.
* @return
*/
public static Range<Distance> between(double minValue, Metric minMetric, double maxValue, Metric maxMetric) {
return between(new Distance(minValue, minMetric), new Distance(maxValue, maxMetric));
}
/**
* Returns the distance value in the current {@link Metric}.
*
@ -138,6 +163,18 @@ public class Distance implements Serializable { @@ -138,6 +163,18 @@ public class Distance implements Serializable {
return this.metric.equals(metric) ? this : new Distance(getNormalizedValue() * metric.getMultiplier(), metric);
}
/*
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(Distance o) {
double difference = this.getNormalizedValue() - o.getNormalizedValue();
return difference == 0 ? 0 : difference > 0 ? 1 : -1;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)

35
src/main/java/org/springframework/data/util/TypeDiscoverer.java

@ -17,6 +17,7 @@ package org.springframework.data.util; @@ -17,6 +17,7 @@ package org.springframework.data.util;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
@ -156,14 +157,8 @@ class TypeDiscoverer<S> implements TypeInformation<S> { @@ -156,14 +157,8 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
*/
public List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor) {
Assert.notNull(constructor);
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>();
for (Type parameterType : constructor.getGenericParameterTypes()) {
result.add(createInfo(parameterType));
}
return result;
Assert.notNull(constructor, "Constructor must not be null!");
return getParameterTypes((Executable) constructor);
}
/*
@ -412,16 +407,8 @@ class TypeDiscoverer<S> implements TypeInformation<S> { @@ -412,16 +407,8 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
*/
public List<TypeInformation<?>> getParameterTypes(Method method) {
Assert.notNull(method);
Type[] parameterTypes = method.getGenericParameterTypes();
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>(parameterTypes.length);
for (Type parameterType : parameterTypes) {
result.add(createInfo(parameterType));
}
return result;
Assert.notNull(method, "Method most not be null!");
return getParameterTypes((Executable) method);
}
/*
@ -492,6 +479,18 @@ class TypeDiscoverer<S> implements TypeInformation<S> { @@ -492,6 +479,18 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
return createInfo(arguments[index]);
}
private List<TypeInformation<?>> getParameterTypes(Executable executable) {
Type[] types = executable.getGenericParameterTypes();
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>(types.length);
for (Type parameterType : types) {
result.add(createInfo(parameterType));
}
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)

113
src/test/java/org/springframework/data/domain/RangeUnitTests.java

@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Unit tests for {@link Range}.
*
* @author Oliver Gierke
* @since 1.10
*/
public class RangeUnitTests {
/**
* @see DATACMNS-651
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullReferenceValuesForContains() {
new Range<Long>(10L, 20L).contains(null);
}
/**
* @see DATACMNS-651
*/
@Test
public void usesBoundsInclusivelyByDefault() {
Range<Long> range = new Range<Long>(10L, 20L);
assertThat(range.contains(10L), is(true));
assertThat(range.contains(20L), is(true));
assertThat(range.contains(15L), is(true));
assertThat(range.contains(5L), is(false));
assertThat(range.contains(25L), is(false));
}
/**
* @see DATACMNS-651
*/
@Test
public void excludesLowerBoundIfConfigured() {
Range<Long> range = new Range<Long>(10L, 20L, false, true);
assertThat(range.contains(10L), is(false));
assertThat(range.contains(20L), is(true));
assertThat(range.contains(15L), is(true));
assertThat(range.contains(5L), is(false));
assertThat(range.contains(25L), is(false));
}
/**
* @see DATACMNS-651
*/
@Test
public void excludesUpperBoundIfConfigured() {
Range<Long> range = new Range<Long>(10L, 20L, true, false);
assertThat(range.contains(10L), is(true));
assertThat(range.contains(20L), is(false));
assertThat(range.contains(15L), is(true));
assertThat(range.contains(5L), is(false));
assertThat(range.contains(25L), is(false));
}
/**
* @see DATACMNS-651
*/
@Test
public void handlesOpenUpperBoundCorrectly() {
Range<Long> range = new Range<Long>(10L, null);
assertThat(range.contains(10L), is(true));
assertThat(range.contains(20L), is(true));
assertThat(range.contains(15L), is(true));
assertThat(range.contains(5L), is(false));
assertThat(range.contains(25L), is(true));
}
/**
* @see DATACMNS-651
*/
@Test
public void handlesOpenLowerBoundCorrectly() {
Range<Long> range = new Range<Long>(null, 20L);
assertThat(range.contains(10L), is(true));
assertThat(range.contains(20L), is(true));
assertThat(range.contains(15L), is(true));
assertThat(range.contains(5L), is(true));
assertThat(range.contains(25L), is(false));
}
}

54
src/test/java/org/springframework/data/geo/DistanceUnitTests.java

@ -15,12 +15,12 @@ @@ -15,12 +15,12 @@
*/
package org.springframework.data.geo;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.number.IsCloseTo.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.geo.Metrics.*;
import org.junit.Test;
import org.springframework.data.domain.Range;
import org.springframework.util.SerializationUtils;
/**
@ -145,4 +145,54 @@ public class DistanceUnitTests { @@ -145,4 +145,54 @@ public class DistanceUnitTests {
public void returnsMetricsAbbreviationAsUnit() {
assertThat(new Distance(10, KILOMETERS).getUnit(), is("km"));
}
/**
* @see DATACMNS-651
*/
@Test
public void createsARangeCorrectly() {
Distance twoKilometers = new Distance(2, KILOMETERS);
Distance tenKilometers = new Distance(10, KILOMETERS);
Range<Distance> range = Distance.between(twoKilometers, tenKilometers);
assertThat(range, is(notNullValue()));
assertThat(range.getLowerBound(), is(twoKilometers));
assertThat(range.getUpperBound(), is(tenKilometers));
}
/**
* @see DATACMNS-651
*/
@Test
public void createsARangeFromPiecesCorrectly() {
Distance twoKilometers = new Distance(2, KILOMETERS);
Distance tenKilometers = new Distance(10, KILOMETERS);
Range<Distance> range = Distance.between(2, KILOMETERS, 10, KILOMETERS);
assertThat(range, is(notNullValue()));
assertThat(range.getLowerBound(), is(twoKilometers));
assertThat(range.getUpperBound(), is(tenKilometers));
}
/**
* @see DATACMNS-651
*/
@Test
public void implementsComparableCorrectly() {
Distance twoKilometers = new Distance(2, KILOMETERS);
Distance tenKilometers = new Distance(10, KILOMETERS);
Distance tenKilometersInMiles = new Distance(6.21371256214785, MILES);
assertThat(tenKilometers.compareTo(tenKilometers), is(0));
assertThat(tenKilometers.compareTo(tenKilometersInMiles), is(0));
assertThat(tenKilometersInMiles.compareTo(tenKilometers), is(0));
assertThat(twoKilometers.compareTo(tenKilometers), is(lessThan(0)));
assertThat(tenKilometers.compareTo(twoKilometers), is(greaterThan(0)));
}
}

Loading…
Cancel
Save