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

@@ -17,6 +17,8 @@ package org.springframework.data.domain;
import java.io.Serializable;
import org.springframework.lang.Nullable;
/**
* Abstract Java Bean implementation of {@code Pageable}.
*
@@ -131,7 +133,7 @@ public abstract class AbstractPageRequest implements Pageable, 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

@@ -25,6 +25,7 @@ import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -171,7 +172,7 @@ abstract class Chunk<T> implements Slice<T>, 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

@@ -32,6 +32,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -201,9 +202,7 @@ public class ExampleMatcher {
propertySpecifier = propertySpecifier.withStringMatcher(genericPropertyMatcher.stringMatcher);
}
if (genericPropertyMatcher.valueTransformer != null) {
propertySpecifier = propertySpecifier.withValueTransformer(genericPropertyMatcher.valueTransformer);
}
propertySpecifier = propertySpecifier.withValueTransformer(genericPropertyMatcher.valueTransformer);
propertySpecifiers.add(propertySpecifier);
@@ -394,8 +393,8 @@ public class ExampleMatcher {
@EqualsAndHashCode
public static class GenericPropertyMatcher {
StringMatcher stringMatcher = null;
Boolean ignoreCase = null;
@Nullable StringMatcher stringMatcher = null;
@Nullable Boolean ignoreCase = null;
PropertyValueTransformer valueTransformer = NoOpPropertyValueTransformer.INSTANCE;
/**
@@ -697,6 +696,7 @@ public class ExampleMatcher {
* @see java.util.function.Function#apply(java.lang.Object)
*/
@Override
@SuppressWarnings("null")
public Optional<Object> apply(Optional<Object> source) {
return source;
}
@@ -715,8 +715,8 @@ public class ExampleMatcher {
public static class PropertySpecifier {
String path;
StringMatcher stringMatcher;
Boolean ignoreCase;
@Nullable StringMatcher stringMatcher;
@Nullable Boolean ignoreCase;
PropertyValueTransformer valueTransformer;
/**
@@ -785,6 +785,7 @@ public class ExampleMatcher {
*
* @return can be {@literal null}.
*/
@Nullable
public StringMatcher getStringMatcher() {
return stringMatcher;
}
@@ -792,6 +793,7 @@ public class ExampleMatcher {
/**
* @return {@literal null} if not set.
*/
@Nullable
public Boolean getIgnoreCase() {
return ignoreCase;
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.domain;
import java.util.Collections;
import java.util.function.Function;
/**
@@ -26,6 +27,27 @@ import java.util.function.Function;
*/
public interface Page<T> extends Slice<T> {
/**
* Creates a new empty {@link Page}.
*
* @return
* @since 2.0
*/
static <T> Page<T> empty() {
return empty(Pageable.unpaged());
}
/**
* Creates a new empty {@link Page} for the given {@link Pageable}.
*
* @param pageable must not be {@literal null}.
* @return
* @since 2.0
*/
static <T> Page<T> empty(Pageable pageable) {
return new PageImpl<>(Collections.emptyList(), pageable, 0);
}
/**
* Returns the number of total pages.
*

View File

@@ -18,6 +18,8 @@ package org.springframework.data.domain;
import java.util.List;
import java.util.function.Function;
import org.springframework.lang.Nullable;
/**
* Basic {@code Page} implementation.
*
@@ -127,7 +129,7 @@ public class PageImpl<T> extends Chunk<T> implements Page<T> {
* @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

@@ -16,6 +16,7 @@
package org.springframework.data.domain;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.lang.Nullable;
/**
* Basic Java Bean implementation of {@code Pageable}.
@@ -145,7 +146,7 @@ public class PageRequest extends AbstractPageRequest {
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;

View File

@@ -18,6 +18,8 @@ package org.springframework.data.domain;
import java.util.List;
import java.util.function.Function;
import org.springframework.lang.Nullable;
/**
* Default implementation of {@link Slice}.
*
@@ -95,7 +97,7 @@ public class SliceImpl<T> extends Chunk<T> {
* @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

@@ -26,6 +26,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -212,9 +213,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
*/
public Sort and(Sort sort) {
if (sort == null) {
return this;
}
Assert.notNull(sort, "Sort must not be null!");
ArrayList<Order> these = new ArrayList<>(this.orders);
@@ -231,6 +230,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
* @param property
* @return
*/
@Nullable
public Order getOrderFor(String property) {
for (Order order : this) {
@@ -255,7 +255,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
* @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;
@@ -400,6 +400,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
private static final long serialVersionUID = 1522511010900108987L;
private static final boolean DEFAULT_IGNORE_CASE = false;
private static final NullHandling DEFAULT_NULL_HANDLING = NullHandling.NATIVE;
private final Direction direction;
private final String property;
@@ -414,7 +415,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
* @param property must not be {@literal null} or empty.
*/
public Order(Direction direction, String property) {
this(direction, property, DEFAULT_IGNORE_CASE, null);
this(direction, property, DEFAULT_IGNORE_CASE, DEFAULT_NULL_HANDLING);
}
/**
@@ -423,7 +424,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
*
* @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}
* @param property must not be {@literal null} or empty.
* @param nullHandling can be {@literal null}, will default to {@link NullHandling#NATIVE}.
* @param nullHandling must not be {@literal null}.
*/
public Order(Direction direction, String property, NullHandling nullHandlingHint) {
this(direction, property, DEFAULT_IGNORE_CASE, nullHandlingHint);
@@ -460,7 +461,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
* @since 2.0
*/
public static Order asc(String property) {
return new Order(Direction.ASC, property, null);
return new Order(Direction.ASC, property, DEFAULT_NULL_HANDLING);
}
/**
@@ -471,7 +472,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
* @since 2.0
*/
public static Order desc(String property) {
return new Order(Direction.DESC, property, null);
return new Order(Direction.DESC, property, DEFAULT_NULL_HANDLING);
}
/**
@@ -481,7 +482,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
* @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}
* @param property must not be {@literal null} or empty.
* @param ignoreCase true if sorting should be case insensitive. false if sorting should be case sensitive.
* @param nullHandling can be {@literal null}, will default to {@link NullHandling#NATIVE}.
* @param nullHandling must not be {@literal null}.
* @since 1.7
*/
private Order(Direction direction, String property, boolean ignoreCase, NullHandling nullHandling) {
@@ -493,7 +494,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
this.direction = direction == null ? DEFAULT_DIRECTION : direction;
this.property = property;
this.ignoreCase = ignoreCase;
this.nullHandling = nullHandling == null ? NullHandling.NATIVE : nullHandling;
this.nullHandling = nullHandling;
}
/**
@@ -655,7 +656,7 @@ public class Sort implements Streamable<org.springframework.data.domain.Sort.Ord
* @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

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2017 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.
@@ -17,8 +17,10 @@ package org.springframework.data.domain.jaxb;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.domain.jaxb.SpringDataJaxb.OrderDto;
import org.springframework.lang.Nullable;
/**
* XmlAdapter to convert {@link Order} instances into {@link OrderDto}s and vice versa.
@@ -33,8 +35,9 @@ public class OrderAdapter extends XmlAdapter<OrderDto, Order> {
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
*/
@Nullable
@Override
public OrderDto marshal(Order order) {
public OrderDto marshal(@Nullable Order order) {
if (order == null) {
return null;
@@ -50,8 +53,21 @@ public class OrderAdapter extends XmlAdapter<OrderDto, Order> {
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
*/
@Nullable
@Override
public Order unmarshal(OrderDto source) {
return source == null ? null : new Order(source.direction, source.property);
public Order unmarshal(@Nullable OrderDto source) {
if (source == null) {
return null;
}
Direction direction = source.direction;
String property = source.property;
if (direction == null || property == null) {
return null;
}
return new Order(direction, property);
}
}

View File

@@ -23,6 +23,7 @@ import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.jaxb.SpringDataJaxb.PageDto;
import org.springframework.hateoas.Link;
import org.springframework.lang.Nullable;
/**
* {@link XmlAdapter} to convert {@link Page} instances into {@link PageDto} instances and vice versa.
@@ -35,8 +36,9 @@ public class PageAdapter extends XmlAdapter<PageDto, Page<Object>> {
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
*/
@Nullable
@Override
public PageDto marshal(Page<Object> source) {
public PageDto marshal(@Nullable Page<Object> source) {
if (source == null) {
return null;
@@ -53,8 +55,9 @@ public class PageAdapter extends XmlAdapter<PageDto, Page<Object>> {
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
*/
@Nullable
@Override
public Page<Object> unmarshal(PageDto v) {
public Page<Object> unmarshal(@Nullable PageDto v) {
return null;
}

View File

@@ -17,13 +17,14 @@ package org.springframework.data.domain.jaxb;
import java.util.Collections;
import javax.annotation.Nonnull;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.jaxb.SpringDataJaxb.OrderDto;
import org.springframework.data.domain.jaxb.SpringDataJaxb.PageRequestDto;
import org.springframework.data.domain.jaxb.SpringDataJaxb.SortDto;
import org.springframework.lang.Nullable;
/**
* {@link XmlAdapter} to convert {@link Pageable} instances int a {@link PageRequestDto} and vice versa.
@@ -37,12 +38,17 @@ class PageableAdapter extends XmlAdapter<PageRequestDto, Pageable> {
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
*/
@Nullable
@Override
public PageRequestDto marshal(Pageable request) {
public PageRequestDto marshal(@Nullable Pageable request) {
SortDto sortDto = SortAdapter.INSTANCE.marshal(request.getSort());
if (request == null) {
return null;
}
PageRequestDto dto = new PageRequestDto();
SortDto sortDto = SortAdapter.INSTANCE.marshal(request.getSort());
dto.orders = sortDto == null ? Collections.emptyList() : sortDto.orders;
dto.page = request.getPageNumber();
dto.size = request.getPageSize();
@@ -54,8 +60,13 @@ class PageableAdapter extends XmlAdapter<PageRequestDto, Pageable> {
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
*/
@Nonnull
@Override
public Pageable unmarshal(PageRequestDto v) {
public Pageable unmarshal(@Nullable PageRequestDto v) {
if (v == null) {
return Pageable.unpaged();
}
if (v.orders.isEmpty()) {
return PageRequest.of(v.page, v.size);

View File

@@ -15,10 +15,12 @@
*/
package org.springframework.data.domain.jaxb;
import javax.annotation.Nonnull;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.jaxb.SpringDataJaxb.SortDto;
import org.springframework.lang.Nullable;
/**
* {@link XmlAdapter} to convert {@link Sort} instances into {@link SortDto} instances and vice versa.
@@ -33,8 +35,9 @@ public class SortAdapter extends XmlAdapter<SortDto, Sort> {
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
*/
@Nullable
@Override
public SortDto marshal(Sort source) {
public SortDto marshal(@Nullable Sort source) {
if (source == null) {
return null;
@@ -50,8 +53,9 @@ public class SortAdapter extends XmlAdapter<SortDto, Sort> {
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
*/
@Nonnull
@Override
public Sort unmarshal(SortDto source) {
public Sort unmarshal(@Nullable SortDto source) {
return source == null ? Sort.unsorted() : Sort.by(SpringDataJaxb.unmarshal(source.orders, OrderAdapter.INSTANCE));
}
}

View File

@@ -36,6 +36,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -84,8 +85,8 @@ public abstract class SpringDataJaxb {
@XmlAccessorType(XmlAccessType.FIELD)
public static class OrderDto {
@XmlAttribute String property;
@XmlAttribute Direction direction;
@Nullable @XmlAttribute String property;
@Nullable @XmlAttribute Direction direction;
}
/**
@@ -97,7 +98,7 @@ public abstract class SpringDataJaxb {
@XmlAccessorType(XmlAccessType.FIELD)
public static class PageDto extends ResourceSupport {
@XmlAnyElement @XmlElementWrapper(name = "content") List<Object> content;
@Nullable @XmlAnyElement @XmlElementWrapper(name = "content") List<Object> content;
}
/**
@@ -136,7 +137,7 @@ public abstract class SpringDataJaxb {
* @return
* @throws Exception
*/
public static <T, S> List<S> marshal(Iterable<T> source, XmlAdapter<S, T> adapter) {
public static <T, S> List<S> marshal(@Nullable Iterable<T> source, XmlAdapter<S, T> adapter) {
Assert.notNull(adapter, "Adapter must not be null!");

View File

@@ -1,13 +1,16 @@
/**
* Central domain abstractions especially to be used in combination with the {@link org.springframework.data.repository.Repository} abstraction.
* Central domain abstractions especially to be used in combination with the
* {@link org.springframework.data.repository.Repository} abstraction.
*
* @see org.springframework.data.repository.Repository
*/
@XmlSchema(xmlns = { @XmlNs(prefix = "sd", namespaceURI = org.springframework.data.domain.jaxb.SpringDataJaxb.NAMESPACE) })
@XmlSchema(
xmlns = { @XmlNs(prefix = "sd", namespaceURI = org.springframework.data.domain.jaxb.SpringDataJaxb.NAMESPACE) })
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value = org.springframework.data.domain.jaxb.PageableAdapter.class, type = Pageable.class),
@XmlJavaTypeAdapter(value = org.springframework.data.domain.jaxb.SortAdapter.class, type = Sort.class),
@XmlJavaTypeAdapter(value = org.springframework.data.domain.jaxb.PageAdapter.class, type = Page.class) })
@org.springframework.lang.NonNullApi
package org.springframework.data.domain.jaxb;
import javax.xml.bind.annotation.XmlNs;
@@ -18,4 +21,3 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

View File

@@ -1,7 +1,8 @@
/**
* Central domain abstractions especially to be used in combination with the {@link org.springframework.data.repository.Repository} abstraction.
* Central domain abstractions especially to be used in combination with the
* {@link org.springframework.data.repository.Repository} abstraction.
*
* @see org.springframework.data.repository.Repository
*/
@org.springframework.lang.NonNullApi
package org.springframework.data.domain;