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

@@ -38,6 +38,7 @@ import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.model.MappingInstantiationException;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -213,7 +214,7 @@ public class ClassGeneratingEntityInstantiator implements EntityInstantiator {
* @return
*/
private <P extends PersistentProperty<P>, T> Object[] extractInvocationArguments(
PreferredConstructor<? extends T, P> constructor, ParameterValueProvider<P> provider) {
@Nullable PreferredConstructor<? extends T, P> constructor, ParameterValueProvider<P> provider) {
if (provider == null || constructor == null || !constructor.hasParameters()) {
return EMPTY_ARRAY;
@@ -484,7 +485,7 @@ public class ClassGeneratingEntityInstantiator implements EntityInstantiator {
*/
private class ByteArrayClassLoader extends ClassLoader {
public ByteArrayClassLoader(ClassLoader parent) {
public ByteArrayClassLoader(@Nullable ClassLoader parent) {
super(parent);
}

View File

@@ -19,6 +19,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
import org.springframework.data.mapping.Alias;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
@@ -77,6 +79,7 @@ public class ConfigurableTypeInformationMapper implements TypeInformationMapper
* (non-Javadoc)
* @see org.springframework.data.convert.TypeInformationMapper#resolveTypeFrom(org.springframework.data.mapping.Alias)
*/
@Nullable
@Override
public TypeInformation<?> resolveTypeFrom(Alias alias) {
return aliasToType.get(alias);

View File

@@ -561,8 +561,11 @@ public class CustomConversions {
} else if (converter instanceof GenericConverter) {
return Streamable.of(GenericConverter.class.cast(converter).getConvertibleTypes())//
.map(it -> register(it, isReading, isWriting));
Set<ConvertiblePair> convertibleTypes = GenericConverter.class.cast(converter).getConvertibleTypes();
return convertibleTypes == null //
? Streamable.empty() //
: Streamable.of(convertibleTypes).map(it -> register(it, isReading, isWriting));
} else if (converter instanceof ConverterFactory) {
@@ -580,7 +583,13 @@ public class CustomConversions {
private Streamable<ConverterRegistration> getRegistrationFor(Object converter, Class<?> type, boolean isReading,
boolean isWriting) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converter.getClass(), type);
Class<? extends Object> converterType = converter.getClass();
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(converterType, type);
if (arguments == null) {
throw new IllegalStateException(String.format("Couldn't resolve type arguments for %s!", converterType));
}
return Streamable.of(register(arguments[0], arguments[1], isReading, isWriting));
}

View File

@@ -27,6 +27,8 @@ import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
@@ -35,6 +37,7 @@ import org.springframework.data.convert.ConverterBuilder.ConverterAware;
import org.springframework.data.convert.ConverterBuilder.ReadingConverterBuilder;
import org.springframework.data.convert.ConverterBuilder.WritingConverterBuilder;
import org.springframework.data.util.Optionals;
import org.springframework.lang.Nullable;
/**
* Builder to easily set up (bi-directional) {@link Converter} instances for Spring Data type mapping using Lambdas. Use
@@ -128,9 +131,10 @@ class DefaultConverterBuilder<S, T>
* (non-Javadoc)
* @see org.springframework.core.convert.converter.GenericConverter#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
*/
@Nullable
@Override
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return function.apply((S) source);
}
@@ -138,6 +142,7 @@ class DefaultConverterBuilder<S, T>
* (non-Javadoc)
* @see org.springframework.core.convert.converter.GenericConverter#getConvertibleTypes()
*/
@Nonnull
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(convertiblePair);

View File

@@ -28,6 +28,7 @@ import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -77,7 +78,7 @@ public class DefaultTypeMapper<S> implements TypeMapper<S> {
* @param additionalMappers must not be {@literal null}.
*/
public DefaultTypeMapper(TypeAliasAccessor<S> accessor,
MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext,
@Nullable MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext,
List<? extends TypeInformationMapper> additionalMappers) {
Assert.notNull(accessor, "Accessor must not be null!");
@@ -109,6 +110,7 @@ public class DefaultTypeMapper<S> implements TypeMapper<S> {
* (non-Javadoc)
* @see org.springframework.data.convert.TypeMapper#readType(java.lang.Object)
*/
@Nullable
public TypeInformation<?> readType(S source) {
Assert.notNull(source, "Source object must not be null!");
@@ -123,6 +125,7 @@ public class DefaultTypeMapper<S> implements TypeMapper<S> {
* @param alias
* @return
*/
@Nullable
private TypeInformation<?> getFromCacheOrCreate(Alias alias) {
return typeCache.computeIfAbsent(alias, getAlias).orElse(null);
}
@@ -153,7 +156,7 @@ public class DefaultTypeMapper<S> implements TypeMapper<S> {
ClassTypeInformation<?> targetType = ClassTypeInformation.from(documentsTargetType);
return (TypeInformation<? extends T>) (basicType != null ? basicType.specialize(targetType) : targetType);
return (TypeInformation<? extends T>) basicType.specialize(targetType);
}
/**
@@ -163,6 +166,7 @@ public class DefaultTypeMapper<S> implements TypeMapper<S> {
* @param source
* @return
*/
@Nullable
private Class<?> getDefaultedTypeToBeUsed(S source) {
TypeInformation<?> documentsTargetTypeInformation = readType(source);
@@ -177,6 +181,7 @@ public class DefaultTypeMapper<S> implements TypeMapper<S> {
* @param source will never be {@literal null}.
* @return
*/
@Nullable
protected TypeInformation<?> getFallbackTypeFor(S source) {
return null;
}

View File

@@ -22,6 +22,8 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.annotation.Nonnull;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
@@ -72,9 +74,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
@Nonnull
@Override
public java.time.LocalDateTime convert(LocalDateTime source) {
return source == null ? null : java.time.LocalDateTime.ofInstant(source.toDate().toInstant(), ZoneId.of("UTC"));
return java.time.LocalDateTime.ofInstant(source.toDate().toInstant(), ZoneId.of("UTC"));
}
}
@@ -82,8 +85,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
@Nonnull
@Override
public Date convert(LocalDate source) {
return source == null ? null : source.toDate();
return source.toDate();
}
}
@@ -91,8 +96,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
@Nonnull
@Override
public Date convert(LocalDateTime source) {
return source == null ? null : source.toDate();
return source.toDate();
}
}
@@ -100,8 +107,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
@Nonnull
@Override
public Date convert(DateTime source) {
return source == null ? null : source.toDate();
return source.toDate();
}
}
@@ -109,8 +118,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
@Nonnull
@Override
public LocalDate convert(Date source) {
return source == null ? null : new LocalDate(source.getTime());
return new LocalDate(source.getTime());
}
}
@@ -118,8 +129,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
@Nonnull
@Override
public LocalDateTime convert(Date source) {
return source == null ? null : new LocalDateTime(source.getTime());
return new LocalDateTime(source.getTime());
}
}
@@ -127,8 +140,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
@Nonnull
@Override
public DateTime convert(Date source) {
return source == null ? null : new DateTime(source.getTime());
return new DateTime(source.getTime());
}
}
@@ -136,15 +151,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Nonnull
@Override
public LocalDateTime convert(java.time.LocalDateTime source) {
return source == null ? null
: LocalDateTime.fromDateFields(
org.springframework.data.convert.Jsr310Converters.LocalDateTimeToDateConverter.INSTANCE.convert(source));
return LocalDateTime.fromDateFields(Jsr310Converters.LocalDateTimeToDateConverter.INSTANCE.convert(source));
}
}
@@ -152,15 +162,10 @@ public abstract class JodaTimeConverters {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Nonnull
@Override
public DateTime convert(java.time.LocalDateTime source) {
return source == null ? null
: new DateTime(
org.springframework.data.convert.Jsr310Converters.LocalDateTimeToDateConverter.INSTANCE.convert(source));
return new DateTime(Jsr310Converters.LocalDateTimeToDateConverter.INSTANCE.convert(source));
}
}
}

View File

@@ -33,6 +33,8 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.annotation.Nonnull;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.ClassUtils;
@@ -92,9 +94,10 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public LocalDateTime convert(Date source) {
return source == null ? null : ofInstant(source.toInstant(), systemDefault());
return ofInstant(source.toInstant(), systemDefault());
}
}
@@ -102,9 +105,10 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public Date convert(LocalDateTime source) {
return source == null ? null : Date.from(source.atZone(systemDefault()).toInstant());
return Date.from(source.atZone(systemDefault()).toInstant());
}
}
@@ -112,9 +116,10 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public LocalDate convert(Date source) {
return source == null ? null : ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalDate();
return ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalDate();
}
}
@@ -122,9 +127,10 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public Date convert(LocalDate source) {
return source == null ? null : Date.from(source.atStartOfDay(systemDefault()).toInstant());
return Date.from(source.atStartOfDay(systemDefault()).toInstant());
}
}
@@ -132,9 +138,10 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public LocalTime convert(Date source) {
return source == null ? null : ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalTime();
return ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalTime();
}
}
@@ -142,9 +149,10 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public Date convert(LocalTime source) {
return source == null ? null : Date.from(source.atDate(LocalDate.now()).atZone(systemDefault()).toInstant());
return Date.from(source.atDate(LocalDate.now()).atZone(systemDefault()).toInstant());
}
}
@@ -152,9 +160,10 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public Instant convert(Date source) {
return source == null ? null : source.toInstant();
return source.toInstant();
}
}
@@ -162,9 +171,10 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public Date convert(Instant source) {
return source == null ? null : Date.from(source.atZone(systemDefault()).toInstant());
return Date.from(source.atZone(systemDefault()).toInstant());
}
}
@@ -173,6 +183,7 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public String convert(ZoneId source) {
return source.toString();
@@ -184,6 +195,7 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public ZoneId convert(String source) {
return ZoneId.of(source);
@@ -195,6 +207,7 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public String convert(Duration duration) {
return duration.toString();
@@ -206,6 +219,7 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public Duration convert(String s) {
return Duration.parse(s);
@@ -217,6 +231,7 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public String convert(Period period) {
return period.toString();
@@ -228,6 +243,7 @@ public abstract class Jsr310Converters {
INSTANCE;
@Nonnull
@Override
public Period convert(String s) {
return Period.parse(s);

View File

@@ -24,6 +24,7 @@ import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -115,6 +116,7 @@ public class MappingContextTypeInformationMapper implements TypeInformationMappe
* (non-Javadoc)
* @see org.springframework.data.convert.TypeInformationMapper#resolveTypeFrom(java.util.Optional)
*/
@Nullable
@Override
public TypeInformation<?> resolveTypeFrom(Alias alias) {

View File

@@ -22,6 +22,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.data.mapping.Alias;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
@@ -45,6 +46,7 @@ public class SimpleTypeInformationMapper implements TypeInformationMapper {
* @return the type to be used for the given {@link String} representation or {@literal null} if nothing found or the
* class cannot be loaded.
*/
@Nullable
@Override
public TypeInformation<?> resolveTypeFrom(Alias alias) {

View File

@@ -27,6 +27,8 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.annotation.Nonnull;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.ClassUtils;
import org.threeten.bp.Instant;
@@ -95,12 +97,13 @@ public abstract class ThreeTenBackPortConverters {
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Nonnull
@Override
public java.time.LocalDateTime convert(LocalDateTime source) {
Date date = toDate(source.atZone(ZoneId.systemDefault()).toInstant());
return source == null ? null : Jsr310Converters.DateToLocalDateTimeConverter.INSTANCE.convert(date);
return Jsr310Converters.DateToLocalDateTimeConverter.INSTANCE.convert(date);
}
}
@@ -108,10 +111,10 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public LocalDateTime convert(Date source) {
return source == null ? null : ofInstant(toInstant(source), systemDefault());
return ofInstant(toInstant(source), systemDefault());
}
}
@@ -119,9 +122,10 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public Date convert(LocalDateTime source) {
return source == null ? null : toDate(source.atZone(systemDefault()).toInstant());
return toDate(source.atZone(systemDefault()).toInstant());
}
}
@@ -129,9 +133,10 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public LocalDate convert(Date source) {
return source == null ? null : ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalDate();
return ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalDate();
}
}
@@ -139,9 +144,10 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public Date convert(LocalDate source) {
return source == null ? null : toDate(source.atStartOfDay(systemDefault()).toInstant());
return toDate(source.atStartOfDay(systemDefault()).toInstant());
}
}
@@ -149,9 +155,10 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public LocalTime convert(Date source) {
return source == null ? null : ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalTime();
return ofInstant(ofEpochMilli(source.getTime()), systemDefault()).toLocalTime();
}
}
@@ -159,9 +166,10 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public Date convert(LocalTime source) {
return source == null ? null : toDate(source.atDate(LocalDate.now()).atZone(systemDefault()).toInstant());
return toDate(source.atDate(LocalDate.now()).atZone(systemDefault()).toInstant());
}
}
@@ -169,9 +177,10 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public Instant convert(Date source) {
return source == null ? null : toInstant(source);
return toInstant(source);
}
}
@@ -179,9 +188,10 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public Date convert(Instant source) {
return source == null ? null : toDate(source.atZone(systemDefault()).toInstant());
return toDate(source.atZone(systemDefault()).toInstant());
}
}
@@ -190,6 +200,7 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public String convert(ZoneId source) {
return source.toString();
@@ -201,6 +212,7 @@ public abstract class ThreeTenBackPortConverters {
INSTANCE;
@Nonnull
@Override
public ZoneId convert(String source) {
return ZoneId.of(source);

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.convert;
import javax.annotation.Nullable;
import org.springframework.data.mapping.Alias;
import org.springframework.data.util.TypeInformation;
@@ -28,15 +30,16 @@ public interface TypeInformationMapper {
/**
* Returns the actual {@link TypeInformation} to be used for the given alias.
*
* @param alias can be {@literal null}.
* @param alias must not be {@literal null}.
* @return
*/
@Nullable
TypeInformation<?> resolveTypeFrom(Alias alias);
/**
* Returns the alias to be used for the given {@link TypeInformation}.
*
* @param type
* @param type must not be {@literal null}.
* @return
*/
Alias createAliasFor(TypeInformation<?> type);

View File

@@ -16,6 +16,7 @@
package org.springframework.data.convert;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
/**
* Interface to define strategies how to store type information in a store specific sink or source.
@@ -30,13 +31,14 @@ public interface TypeMapper<S> {
* @param source must not be {@literal null}.
* @return
*/
@Nullable
TypeInformation<?> readType(S source);
/**
* Returns the {@link TypeInformation} from the given source if it is a more concrete type than the given default one.
*
* @param source must not be {@literal null}.
* @param defaultType
* @param defaultType must not be {@literal null}.
* @return
*/
<T> TypeInformation<? extends T> readType(S source, TypeInformation<T> defaultType);

View File

@@ -3,4 +3,5 @@
*
* @see org.springframework.data.convert.EntityConverter
*/
package org.springframework.data.convert;
@org.springframework.lang.NonNullApi
package org.springframework.data.convert;