DATACMNS-1114 - Introduced usage of nullable annotations for API validation.

Marked all packages with Spring Frameworks @NonNullApi. Added Spring's @Nullable to methods, parameters and fields that take or produce null values. Adapted using code to make sure the IDE can evaluate the null flow properly. Fixed Javadoc in places where an invalid null handling policy was advertised. Strengthened null requirements for types that expose null-instances.

Removed null handling from converters for JodaTime and ThreeTenBP. Introduced factory methods Page.empty() and Page.empty(Pageable). Introduced default methods getRequiredGetter(), …Setter() and …Field() on PersistentProperty to allow non-nullable lookups of members. The same for TypeInformation.getrequiredActualType(), …SuperTypeInformation().

Tweaked PersistentPropertyCreator.addPropertiesForRemainingDescriptors() to filter unsuitable PropertyDescriptors before actually trying to create a Property instance from them as the new stronger nullability requirements would cause exceptions downstream.

Lazy.get() now expects a non-null return value. Clients being able to cope with null need to call ….orElse(…).

Original pull request: #232.
This commit is contained in:
Oliver Gierke
2017-06-27 08:41:16 +02:00
parent d9b16d8a27
commit 049970874d
234 changed files with 2274 additions and 1179 deletions

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.geo;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -109,7 +110,7 @@ public class Box implements Shape {
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.geo;
import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.util.Assert;
@@ -26,6 +28,7 @@ import org.springframework.util.Assert;
* @author Thomas Darimont
* @since 1.8
*/
@EqualsAndHashCode
public class Circle implements Shape {
private static final long serialVersionUID = 5215611530535947924L;
@@ -98,39 +101,4 @@ public class Circle implements Shape {
public String toString() {
return String.format("Circle: [center=%s, radius=%s]", center, radius);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Circle)) {
return false;
}
Circle that = (Circle) obj;
return this.center.equals(that.center) && this.radius.equals(that.radius);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * center.hashCode();
result += 31 * radius.hashCode();
return result;
}
}

View File

@@ -15,9 +15,13 @@
*/
package org.springframework.data.geo;
import lombok.Value;
import java.io.Serializable;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Range.Bound;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -27,11 +31,19 @@ import org.springframework.util.Assert;
* @author Thomas Darimont
* @since 1.8
*/
@Value
public class Distance implements Serializable, Comparable<Distance> {
private static final long serialVersionUID = 2460886201934027744L;
/**
* The distance value in the current {@link Metric}.
*/
private final double value;
/**
* The {@link Metric} of the {@link Distance}.
*/
private final Metric metric;
/**
@@ -47,12 +59,14 @@ public class Distance implements Serializable, Comparable<Distance> {
* Creates a new {@link Distance} with the given {@link Metric}.
*
* @param value
* @param metric can be {@literal null}.
* @param metric must not be {@literal null}.
*/
public Distance(double value, Metric metric) {
Assert.notNull(metric, "Metric must not be null!");
this.value = value;
this.metric = metric == null ? Metrics.NEUTRAL : metric;
this.metric = metric;
}
/**
@@ -63,7 +77,7 @@ public class Distance implements Serializable, Comparable<Distance> {
* @return will never be {@literal null}.
*/
public static Range<Distance> between(Distance min, Distance max) {
return new Range<>(min, max);
return Range.from(Bound.inclusive(min)).to(Bound.inclusive(max));
}
/**
@@ -79,15 +93,6 @@ public class Distance implements Serializable, Comparable<Distance> {
return between(new Distance(minValue, minMetric), new Distance(maxValue, maxMetric));
}
/**
* Returns the distance value in the current {@link Metric}.
*
* @return the value
*/
public double getValue() {
return value;
}
/**
* Returns the normalized value regarding the underlying {@link Metric}.
*
@@ -97,15 +102,6 @@ public class Distance implements Serializable, Comparable<Distance> {
return value / metric.getMultiplier();
}
/**
* Returns the {@link Metric} of the {@link Distance}.
*
* @return the metric
*/
public Metric getMetric() {
return metric;
}
/**
* Returns a {@link String} representation of the unit the distance is in.
*
@@ -160,6 +156,7 @@ public class Distance implements Serializable, Comparable<Distance> {
public Distance in(Metric metric) {
Assert.notNull(metric, "Metric must not be null!");
return this.metric.equals(metric) ? this : new Distance(getNormalizedValue() * metric.getMultiplier(), metric);
}
@@ -168,49 +165,19 @@ public class Distance implements Serializable, Comparable<Distance> {
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(Distance o) {
public int compareTo(@Nullable Distance that) {
double difference = this.getNormalizedValue() - o.getNormalizedValue();
if (that == null) {
return 1;
}
double difference = this.getNormalizedValue() - that.getNormalizedValue();
return difference == 0 ? 0 : difference > 0 ? 1 : -1;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Distance)) {
return false;
}
Distance that = (Distance) obj;
return this.value == that.value && this.metric.equals(that.metric);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * Double.doubleToLongBits(value);
result += 31 * metric.hashCode();
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override

View File

@@ -15,9 +15,12 @@
*/
package org.springframework.data.geo;
import lombok.Getter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
@@ -31,7 +34,11 @@ import org.springframework.util.ObjectUtils;
public class GeoPage<T> extends PageImpl<GeoResult<T>> {
private static final long serialVersionUID = -5655267379242128600L;
private final Distance averageDistance;
/**
* The average distance of the underlying results.
*/
private final @Getter Distance averageDistance;
/**
* Creates a new {@link GeoPage} from the given {@link GeoResults}.
@@ -59,21 +66,12 @@ public class GeoPage<T> extends PageImpl<GeoResult<T>> {
this.averageDistance = results.getAverageDistance();
}
/**
* Returns the average distance of the underlying results.
*
* @return the averageDistance
*/
public Distance getAverageDistance() {
return averageDistance;
}
/*
* (non-Javadoc)
* @see org.springframework.data.domain.PageImpl#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;

View File

@@ -15,9 +15,10 @@
*/
package org.springframework.data.geo;
import java.io.Serializable;
import lombok.NonNull;
import lombok.Value;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* Value object capturing some arbitrary object plus a distance.
@@ -26,80 +27,13 @@ import org.springframework.util.Assert;
* @author Thomas Darimont
* @since 1.8
*/
@Value
public class GeoResult<T> implements Serializable {
private static final long serialVersionUID = 1637452570977581370L;
private final T content;
private final Distance distance;
/**
* Creates a new {@link GeoResult} for the given content and distance.
*
* @param content must not be {@literal null}.
* @param distance must not be {@literal null}.
*/
public GeoResult(T content, Distance distance) {
Assert.notNull(content, "Content must not be null!");
Assert.notNull(distance, "Distance must not be null!");
this.content = content;
this.distance = distance;
}
/**
* Returns the actual content object.
*
* @return the content
*/
public T getContent() {
return content;
}
/**
* Returns the distance the actual content object has from the origin.
*
* @return the distance
*/
public Distance getDistance() {
return distance;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof GeoResult)) {
return false;
}
GeoResult<?> that = (GeoResult<?>) obj;
return this.content.equals(that.content) && this.distance.equals(that.distance);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * distance.hashCode();
result += 31 * content.hashCode();
return result;
}
@NonNull T content;
@NonNull Distance distance;
/*
* (non-Javadoc)

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.geo;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
@@ -31,6 +33,7 @@ import org.springframework.util.StringUtils;
* @author Thomas Darimont
* @since 1.8
*/
@EqualsAndHashCode
public class GeoResults<T> implements Iterable<GeoResult<T>>, Serializable {
private static final long serialVersionUID = 8347363491300219485L;
@@ -45,7 +48,7 @@ public class GeoResults<T> implements Iterable<GeoResult<T>>, Serializable {
* @param results must not be {@literal null}.
*/
public GeoResults(List<? extends GeoResult<T>> results) {
this(results, (Metric) null);
this(results, Metrics.NEUTRAL);
}
/**
@@ -63,12 +66,13 @@ public class GeoResults<T> implements Iterable<GeoResult<T>>, Serializable {
* Creates a new {@link GeoResults} instance from the given {@link GeoResult}s and average distance.
*
* @param results must not be {@literal null}.
* @param averageDistance can be {@literal null}.
* @param averageDistance must not be {@literal null}.
*/
@PersistenceConstructor
public GeoResults(List<? extends GeoResult<T>> results, Distance averageDistance) {
Assert.notNull(results, "Results must not be null!");
Assert.notNull(averageDistance, "Average Distance must not be null!");
this.results = results;
this.averageDistance = averageDistance;
@@ -101,41 +105,6 @@ public class GeoResults<T> implements Iterable<GeoResult<T>>, Serializable {
return (Iterator<GeoResult<T>>) results.iterator();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof GeoResults)) {
return false;
}
GeoResults<?> that = (GeoResults<?>) obj;
return this.results.equals(that.results) && this.averageDistance.equals(that.averageDistance);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * results.hashCode();
result += 31 * averageDistance.hashCode();
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
@@ -148,16 +117,17 @@ public class GeoResults<T> implements Iterable<GeoResult<T>>, Serializable {
private static Distance calculateAverageDistance(List<? extends GeoResult<?>> results, Metric metric) {
Assert.notNull(results, "Results must not be null!");
Assert.notNull(metric, "Metric must not be null!");
if (results.isEmpty()) {
return new Distance(0, metric);
}
double averageDistance = 0;
double averageDistance = results.stream()//
.mapToDouble(it -> it.getDistance().getValue())//
.average().orElse(0);
for (GeoResult<?> result : results) {
averageDistance += result.getDistance().getValue();
}
return new Distance(averageDistance / results.size(), metric);
return new Distance(averageDistance, metric);
}
}

View File

@@ -19,6 +19,7 @@ import java.io.Serializable;
import java.util.Locale;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -102,7 +103,7 @@ public class Point implements Serializable {
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.geo;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -32,6 +34,7 @@ import org.springframework.util.StringUtils;
* @author Thomas Darimont
* @since 1.8
*/
@EqualsAndHashCode
public class Polygon implements Iterable<Point>, Shape {
private static final long serialVersionUID = -2705040068154648988L;
@@ -98,35 +101,6 @@ public class Polygon implements Iterable<Point>, Shape {
return this.points.iterator();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Polygon)) {
return false;
}
Polygon that = (Polygon) obj;
return this.points.equals(that.points);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return points.hashCode();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()

View File

@@ -27,6 +27,7 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Metrics;
import org.springframework.format.Formatter;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
@@ -60,6 +61,7 @@ public enum DistanceFormatter implements Converter<String, Distance>, Formatter<
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Nullable
@Override
public final Distance convert(String source) {
return source == null ? null : doConvert(source.trim().toLowerCase(Locale.US));
@@ -80,7 +82,7 @@ public enum DistanceFormatter implements Converter<String, Distance>, Formatter<
*/
@Override
public Distance parse(String text, Locale locale) throws ParseException {
return !StringUtils.hasText(text) ? null : doConvert(text.trim().toLowerCase(locale));
return doConvert(text.trim().toLowerCase(locale));
}
/**

View File

@@ -18,11 +18,12 @@ package org.springframework.data.geo.format;
import java.text.ParseException;
import java.util.Locale;
import javax.annotation.Nonnull;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.data.geo.Point;
import org.springframework.format.Formatter;
import org.springframework.util.StringUtils;
/**
* Converter to parse two comma-separated doubles into a {@link Point}.
@@ -41,6 +42,7 @@ public enum PointFormatter implements Converter<String, Point>, Formatter<Point>
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Nonnull
@Override
public Point convert(String source) {
@@ -77,6 +79,6 @@ public enum PointFormatter implements Converter<String, Point>, Formatter<Point>
*/
@Override
public Point parse(String text, Locale locale) throws ParseException {
return !StringUtils.hasText(text) ? null : convert(text);
return convert(text);
}
}

View File

@@ -0,0 +1,8 @@
/**
* Formatters for geo-spatial types.
*
* @author Oliver Gierke
* @since 1.8
*/
@org.springframework.lang.NonNullApi
package org.springframework.data.geo.format;

View File

@@ -5,4 +5,5 @@
* @author Oliver Gierke
* @since 1.8
*/
package org.springframework.data.geo;
@org.springframework.lang.NonNullApi
package org.springframework.data.geo;