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

@@ -28,6 +28,7 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
@@ -41,8 +42,8 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
private final Iterable<Class<? extends Annotation>> annotationTypess;
private final boolean considerInterfaces;
private ResourceLoader resourceLoader;
private Environment environment;
private @Nullable ResourceLoader resourceLoader;
private @Nullable Environment environment;
/**
* Creates a new {@link AnnotatedTypeScanner} for the given annotation types.
@@ -107,12 +108,22 @@ public class AnnotatedTypeScanner implements ResourceLoaderAware, EnvironmentAwa
Set<Class<?>> types = new HashSet<>();
ResourceLoader loader = resourceLoader;
ClassLoader classLoader = loader == null ? null : loader.getClassLoader();
for (String basePackage : basePackages) {
for (BeanDefinition definition : provider.findCandidateComponents(basePackage)) {
String beanClassName = definition.getBeanClassName();
if (beanClassName == null) {
throw new IllegalStateException(
String.format("Unable to obtain bean class name from bean definition %s!", definition));
}
try {
types.add(ClassUtils.forName(definition.getBeanClassName(),
resourceLoader == null ? null : resourceLoader.getClassLoader()));
types.add(ClassUtils.forName(beanClassName, classLoader));
} catch (ClassNotFoundException o_O) {
throw new IllegalStateException(o_O);
}

View File

@@ -17,9 +17,9 @@ package org.springframework.data.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Optional;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
@@ -34,7 +34,7 @@ import org.springframework.util.ReflectionUtils.FieldCallback;
public class AnnotationDetectionFieldCallback implements FieldCallback {
private final Class<? extends Annotation> annotationType;
private Optional<Field> field = Optional.empty();
private @Nullable Field field;
/**
* Creates a new {@link AnnotationDetectionFieldCallback} scanning for an annotation of the given type.
@@ -54,36 +54,65 @@ public class AnnotationDetectionFieldCallback implements FieldCallback {
*/
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
if (this.field.isPresent()) {
if (this.field != null) {
return;
}
if (AnnotatedElementUtils.findMergedAnnotation(field, annotationType) != null) {
ReflectionUtils.makeAccessible(field);
this.field = Optional.of(field);
this.field = field;
}
}
/**
* Returns the detected field.
*
* @return the field
*/
@Nullable
public Field getField() {
return field;
}
/**
* Returns the field that was detected.
*
* @return
* @throws IllegalStateException in case no field with the configured annotation was found.
*/
public Field getRequiredField() {
Field field = this.field;
if (field == null) {
throw new IllegalStateException(String.format("No field found for annotation %s!", annotationType));
}
return field;
}
/**
* Returns the type of the field.
*
* @return
*/
public Optional<Class<?>> getType() {
return field.map(Field::getType);
@Nullable
public Class<?> getType() {
Field field = this.field;
return field == null ? null : field.getType();
}
/**
* Returns the type of the field or throws an {@link IllegalArgumentException} if no field could be found.
*
* @return
* @throws IllegalStateException
* @throws IllegalStateException in case no field with the configured annotation was found.
*/
public Class<?> getRequiredType() {
return getType().orElseThrow(() -> new IllegalStateException(
String.format("Unable to obtain type! Didn't find field with annotation %s!", annotationType)));
return getRequiredField().getType();
}
/**
@@ -92,11 +121,18 @@ public class AnnotationDetectionFieldCallback implements FieldCallback {
* @param source must not be {@literal null}.
* @return
*/
@Nullable
@SuppressWarnings("unchecked")
public <T> Optional<T> getValue(Object source) {
public <T> T getValue(Object source) {
Assert.notNull(source, "Source object must not be null!");
return field.map(it -> (T) ReflectionUtils.getField(it, source));
Field field = this.field;
if (field == null) {
return null;
}
return (T) ReflectionUtils.getField(field, source);
}
}

View File

@@ -19,6 +19,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils.MethodCallback;
@@ -35,8 +36,8 @@ public class AnnotationDetectionMethodCallback<A extends Annotation> implements
private final boolean enforceUniqueness;
private final Class<A> annotationType;
private Method foundMethod;
private A annotation;
private @Nullable Method foundMethod;
private @Nullable A annotation;
/**
* Creates a new {@link AnnotationDetectionMethodCallback} for the given annotation type.
@@ -64,13 +65,32 @@ public class AnnotationDetectionMethodCallback<A extends Annotation> implements
/**
* @return the method
*/
@Nullable
public Method getMethod() {
return foundMethod;
}
/**
* Returns the method with the configured annotation.
*
* @return
* @throws IllegalStateException in case no method with the configured annotation was found.
*/
public Method getRequiredMethod() {
Method method = this.foundMethod;
if (method == null) {
throw new IllegalStateException(String.format("No method with annotation %s found!", annotationType));
}
return method;
}
/**
* @return the annotation
*/
@Nullable
public A getAnnotation() {
return annotation;
}
@@ -100,8 +120,8 @@ public class AnnotationDetectionMethodCallback<A extends Annotation> implements
if (foundAnnotation != null) {
if (foundMethod != null && enforceUniqueness) {
throw new IllegalStateException(String.format(MULTIPLE_FOUND, foundAnnotation.getClass().getName(), foundMethod,
method));
throw new IllegalStateException(
String.format(MULTIPLE_FOUND, foundAnnotation.getClass().getName(), foundMethod, method));
}
this.annotation = foundAnnotation;

View File

@@ -22,6 +22,7 @@ import java.lang.reflect.Field;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.lang.Nullable;
/**
* Custom extension of {@link BeanWrapperImpl} that falls back to direct field access in case the object or type being
@@ -44,6 +45,7 @@ public class DirectFieldAccessFallbackBeanWrapper extends BeanWrapperImpl {
* @see org.springframework.beans.BeanWrapperImpl#getPropertyValue(java.lang.String)
*/
@Override
@Nullable
public Object getPropertyValue(String propertyName) {
try {
@@ -67,7 +69,7 @@ public class DirectFieldAccessFallbackBeanWrapper extends BeanWrapperImpl {
* @see org.springframework.beans.BeanWrapperImpl#setPropertyValue(java.lang.String, java.lang.Object)
*/
@Override
public void setPropertyValue(String propertyName, Object value) {
public void setPropertyValue(String propertyName, @Nullable Object value) {
try {
super.setPropertyValue(propertyName, value);

View File

@@ -21,6 +21,8 @@ import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;
import javax.annotation.Nonnull;
/**
* Special {@link TypeDiscoverer} handling {@link GenericArrayType}s.
*
@@ -60,6 +62,7 @@ class GenericArrayTypeInformation<S> extends ParentTypeAwareTypeInformation<S> {
* @see org.springframework.data.util.TypeDiscoverer#doGetComponentType()
*/
@Override
@Nonnull
protected TypeInformation<?> doGetComponentType() {
Type componentType = type.getGenericComponentType();

View File

@@ -21,6 +21,7 @@ import lombok.RequiredArgsConstructor;
import java.util.function.Function;
import java.util.function.Supplier;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -37,7 +38,8 @@ import org.springframework.util.Assert;
public class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private T value;
private @Nullable T value = null;
private boolean resolved = false;
/**
* Creates a new {@link Lazy} to produce an object lazily.
@@ -58,13 +60,44 @@ public class Lazy<T> implements Supplier<T> {
*/
public T get() {
T value = getNullable();
if (value == null) {
this.value = supplier.get();
throw new IllegalStateException("Expected lazy evaluation to yield a non-null value but got null!");
}
return value;
}
/**
* Returns the value of the lazy computation or the given default value in case the computation yields
* {@literal null}.
*
* @param value
* @return
*/
@Nullable
public T orElse(@Nullable T value) {
return orElseGet(() -> value);
}
/**
* Returns the value of the lazy computation or the value produced by the given {@link Supplier} in case the original
* value is {@literal null}.
*
* @param supplier must not be {@literal null}.
* @return
*/
@Nullable
public T orElseGet(Supplier<T> supplier) {
Assert.notNull(supplier, "Default value supplier must not be null!");
T nullable = getNullable();
return nullable == null ? supplier.get() : nullable;
}
/**
* Creates a new {@link Lazy} with the given {@link Function} lazily applied to the current one.
*
@@ -90,4 +123,26 @@ public class Lazy<T> implements Supplier<T> {
return Lazy.of(() -> function.apply(get()).get());
}
/**
* Returns the value of the lazy evaluation.
*
* @return
*/
@Nullable
private T getNullable() {
T value = this.value;
if (this.resolved) {
return value;
}
value = supplier.get();
this.value = value;
this.resolved = true;
return value;
}
}

View File

@@ -20,12 +20,14 @@ import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
@@ -39,7 +41,7 @@ import org.springframework.util.StringUtils;
class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T> {
private final ParameterizedType type;
private Boolean resolved;
private final Lazy<Boolean> resolved;
/**
* Creates a new {@link ParameterizedTypeInformation} for the given {@link Type} and parent {@link TypeDiscoverer}.
@@ -51,7 +53,9 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
Map<TypeVariable<?>, Type> typeVariableMap) {
super(type, parent, typeVariableMap);
this.type = type;
this.resolved = Lazy.of(() -> isResolvedCompletely());
}
/*
@@ -59,6 +63,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
* @see org.springframework.data.util.TypeDiscoverer#doGetMapValueType()
*/
@Override
@Nullable
protected TypeInformation<?> doGetMapValueType() {
if (Map.class.isAssignableFrom(getType())) {
@@ -127,7 +132,8 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
: target.getSuperTypeInformation(rawType);
List<TypeInformation<?>> myParameters = getTypeArguments();
List<TypeInformation<?>> typeParameters = otherTypeInformation.getTypeArguments();
List<TypeInformation<?>> typeParameters = otherTypeInformation == null ? Collections.emptyList()
: otherTypeInformation.getTypeArguments();
if (myParameters.size() != typeParameters.size()) {
return false;
@@ -147,6 +153,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
* @see org.springframework.data.util.TypeDiscoverer#doGetComponentType()
*/
@Override
@Nullable
protected TypeInformation<?> doGetComponentType() {
return createInfo(type.getActualTypeArguments()[0]);
}
@@ -156,7 +163,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
* @see org.springframework.data.util.ParentTypeAwareTypeInformation#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (obj == this) {
return true;
@@ -168,7 +175,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
ParameterizedTypeInformation<?> that = (ParameterizedTypeInformation<?>) obj;
if (this.isResolvedCompletely() && that.isResolvedCompletely()) {
if (this.isResolved() && that.isResolved()) {
return this.type.equals(that.type);
}
@@ -181,7 +188,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
*/
@Override
public int hashCode() {
return isResolvedCompletely() ? this.type.hashCode() : super.hashCode();
return isResolved() ? this.type.hashCode() : super.hashCode();
}
/*
@@ -195,16 +202,16 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
StringUtils.collectionToCommaDelimitedString(getTypeArguments()));
}
private boolean isResolvedCompletely() {
private boolean isResolved() {
return resolved.get();
}
if (resolved != null) {
return resolved;
}
private boolean isResolvedCompletely() {
Type[] typeArguments = type.getActualTypeArguments();
if (typeArguments.length == 0) {
return cacheAndReturn(false);
return false;
}
for (Type typeArgument : typeArguments) {
@@ -213,21 +220,15 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
if (info instanceof ParameterizedTypeInformation) {
if (!((ParameterizedTypeInformation<?>) info).isResolvedCompletely()) {
return cacheAndReturn(false);
return false;
}
}
if (!(info instanceof ClassTypeInformation)) {
return cacheAndReturn(false);
return false;
}
}
return cacheAndReturn(true);
}
private boolean cacheAndReturn(boolean resolved) {
this.resolved = resolved;
return resolved;
return true;
}
}

View File

@@ -20,6 +20,8 @@ import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
import org.springframework.lang.Nullable;
/**
* Base class for {@link TypeInformation} implementations that need parent type awareness.
*
@@ -78,12 +80,16 @@ public abstract class ParentTypeAwareTypeInformation<S> extends TypeDiscoverer<S
* @see org.springframework.data.util.TypeDiscoverer#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (!super.equals(obj)) {
return false;
}
if (obj == null) {
return false;
}
if (!this.getClass().equals(obj.getClass())) {
return false;
}

View File

@@ -33,6 +33,7 @@ import java.util.stream.Stream;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils.FieldFilter;
@@ -117,7 +118,8 @@ public class ReflectionUtils {
* @param filter must not be {@literal null}.
* @return the field matching the filter or {@literal null} in case no field could be found.
*/
public static Field findField(Class<?> type, final FieldFilter filter) {
@Nullable
public static Field findField(Class<?> type, FieldFilter filter) {
return findField(type, new DescribedFieldFilter() {
@@ -141,6 +143,7 @@ public class ReflectionUtils {
* @return the field matching the given {@link DescribedFieldFilter} or {@literal null} if none found.
* @throws IllegalStateException in case more than one matching field is found
*/
@Nullable
public static Field findField(Class<?> type, DescribedFieldFilter filter) {
return findField(type, filter, true);
}
@@ -155,6 +158,7 @@ public class ReflectionUtils {
* @return the field matching the given {@link DescribedFieldFilter} or {@literal null} if none found.
* @throws IllegalStateException if enforceUniqueness is true and more than one matching field is found
*/
@Nullable
public static Field findField(Class<?> type, DescribedFieldFilter filter, boolean enforceUniqueness) {
Assert.notNull(type, "Type must not be null!");
@@ -188,6 +192,25 @@ public class ReflectionUtils {
return foundField;
}
/**
* Finds the field of the given name on the given type.
*
* @param type must not be {@literal null}.
* @param name must not be {@literal null} or empty.
* @return
* @throws IllegalArgumentException in case the field can't be found.
*/
public static Field findRequiredField(Class<?> type, String name) {
Field result = org.springframework.util.ReflectionUtils.findField(type, name);
if (result == null) {
throw new IllegalArgumentException(String.format("Unable to find field %s on %s!", name, type));
}
return result;
}
/**
* Sets the given field on the given object to the given value. Will make sure the given field is accessible.
*
@@ -195,7 +218,7 @@ public class ReflectionUtils {
* @param target must not be {@literal null}.
* @param value
*/
public static void setField(Field field, Object target, Object value) {
public static void setField(Field field, Object target, @Nullable Object value) {
org.springframework.util.ReflectionUtils.makeAccessible(field);
org.springframework.util.ReflectionUtils.setField(field, target, value);
@@ -218,6 +241,32 @@ public class ReflectionUtils {
.findFirst();
}
/**
* Returns the method with the given name of the given class and parameter types.
*
* @param type must not be {@literal null}.
* @param name must not be {@literal null}.
* @param parameterTypes must not be {@literal null}.
* @return
* @throws IllegalArgumentException in case the method cannot be resolved.
*/
public static Method findRequiredMethod(Class<?> type, String name, Class<?>... parameterTypes) {
Method result = org.springframework.util.ReflectionUtils.findMethod(type, name, parameterTypes);
if (result == null) {
String parameterTypeNames = Arrays.stream(parameterTypes) //
.map(Object::toString) //
.collect(Collectors.joining(", "));
throw new IllegalArgumentException(
String.format("Unable to find method %s(%s)on %s!", name, parameterTypeNames, type));
}
return result;
}
/**
* Returns a {@link Stream} of the return and parameters types of the given {@link Method}.
*

View File

@@ -43,6 +43,7 @@ import java.util.stream.Collectors;
import org.springframework.beans.BeanUtils;
import org.springframework.core.GenericTypeResolver;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -211,6 +212,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getProperty(java.lang.String)
*/
@Nullable
public TypeInformation<?> getProperty(String fieldname) {
int separatorIndex = fieldname.indexOf('.');
@@ -237,6 +239,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @param fieldname
* @return
*/
@SuppressWarnings("null")
private Optional<TypeInformation<?>> getPropertyInformation(String fieldname) {
Class<?> rawType = getType();
@@ -280,6 +283,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @param descriptor must not be {@literal null}
* @return
*/
@Nullable
private static Type getGenericType(PropertyDescriptor descriptor) {
Method method = descriptor.getReadMethod();
@@ -319,6 +323,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getActualType()
*/
@Nullable
public TypeInformation<?> getActualType() {
if (isMap()) {
@@ -353,10 +358,12 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getMapValueType()
*/
@Nullable
public TypeInformation<?> getMapValueType() {
return valueType.get();
return valueType.orElse(null);
}
@Nullable
protected TypeInformation<?> doGetMapValueType() {
return isMap() ? getTypeArgument(getBaseType(MAP_TYPES), 1)
: getTypeArguments().stream().skip(1).findFirst().orElse(null);
@@ -377,10 +384,12 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getComponentType()
*/
@Nullable
public final TypeInformation<?> getComponentType() {
return componentType.get();
return componentType.orElse(null);
}
@Nullable
protected TypeInformation<?> doGetComponentType() {
Class<S> rawType = getType();
@@ -429,6 +438,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getSuperTypeInformation(java.lang.Class)
*/
@Nullable
public TypeInformation<?> getSuperTypeInformation(Class<?> superType) {
Class<?> rawType = getType();
@@ -478,7 +488,10 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @see org.springframework.data.util.TypeInformation#isAssignableFrom(org.springframework.data.util.TypeInformation)
*/
public boolean isAssignableFrom(TypeInformation<?> target) {
return target.getSuperTypeInformation(getType()).equals(this);
TypeInformation<?> superTypeInformation = target.getSuperTypeInformation(getType());
return superTypeInformation == null ? false : superTypeInformation.equals(this);
}
/*
@@ -498,6 +511,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
: createInfo(new SyntheticParamterizedType(type, arguments)));
}
@Nullable
private TypeInformation<?> getTypeArgument(Class<?> bound, int index) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(getType(), bound);
@@ -527,7 +541,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (obj == this) {
return true;
@@ -582,6 +596,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @see java.lang.reflect.ParameterizedType#getOwnerType()
*/
@Override
@Nullable
public Type getOwnerType() {
return null;
}

View File

@@ -19,6 +19,8 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import org.springframework.lang.Nullable;
/**
* Interface to access property types and resolving generics on the way. Starting with a {@link ClassTypeInformation}
* you can traverse properties using {@link #getProperty(String)} to access type information.
@@ -43,6 +45,7 @@ public interface TypeInformation<S> {
* @param property
* @return
*/
@Nullable
TypeInformation<?> getProperty(String property);
/**
@@ -80,6 +83,7 @@ public interface TypeInformation<S> {
*
* @return
*/
@Nullable
TypeInformation<?> getComponentType();
/**
@@ -96,14 +100,14 @@ public interface TypeInformation<S> {
TypeInformation<?> componentType = getComponentType();
if (componentType != null) {
return getComponentType();
return componentType;
}
throw new IllegalStateException(String.format("Can't resolve required component type for %s!", getType()));
}
/**
* Returns whether the property is a {@link java.util.Map}. If this returns {@literal true} you can expect
* Returns whether the property is a {@ link java.util.Map}. If this returns {@literal true} you can expect
* {@link #getComponentType()} as well as {@link #getMapValueType()} to return something not {@literal null}.
*
* @return
@@ -115,6 +119,7 @@ public interface TypeInformation<S> {
*
* @return
*/
@Nullable
TypeInformation<?> getMapValueType();
/**
@@ -122,7 +127,8 @@ public interface TypeInformation<S> {
* {@link IllegalStateException} if the map value type cannot be resolved.
*
* @return
* @throws IllegalStateException if the map value type cannot be resolved.
* @throws IllegalStateException if the map value type cannot be resolved, usually due to the current
* {@link java.util.Map} type being a raw one.
* @since 2.0
*/
default TypeInformation<?> getRequiredMapValueType() {
@@ -155,10 +161,33 @@ public interface TypeInformation<S> {
* Transparently returns the {@link java.util.Map} value type if the type is a {@link java.util.Map}, returns the
* component type if the type {@link #isCollectionLike()} or the simple type if none of this applies.
*
* @return
* @return the map value, collection component type or the current type, {@literal null} it the current type is a raw
* {@link java.util.Map} or {@link java.util.Collection}.
*/
@Nullable
TypeInformation<?> getActualType();
/**
* Transparently returns the {@link java.util.Map} value type if the type is a {@link java.util.Map}, returns the
* component type if the type {@link #isCollectionLike()} or the simple type if none of this applies.
*
* @return
* @throws IllegalArgumentException if the current type is a raw {@link java.util.Map} or {@link java.util.Collection}
* and no value or component type is available.
* @since 2.0
*/
default TypeInformation<?> getRequiredActualType() {
TypeInformation<?> result = getActualType();
if (result == null) {
throw new IllegalStateException(
"Expected to be able to resolve a type but got null! This usually stems from types implementing raw Map or Collection interfaces!");
}
return result;
}
/**
* Returns a {@link TypeInformation} for the return type of the given {@link Method}. Will potentially resolve
* generics information against the current types type parameter bindings.
@@ -183,8 +212,30 @@ public interface TypeInformation<S> {
* @return the {@link TypeInformation} for the given raw super type or {@literal null} in case the current
* {@link TypeInformation} does not implement the given type.
*/
@Nullable
TypeInformation<?> getSuperTypeInformation(Class<?> superType);
/**
* Returns the {@link TypeInformation} for the given raw super type.
*
* @param superType must not be {@literal null}.
* @return the {@link TypeInformation} for the given raw super type.
* @throws IllegalArgumentException in case the current {@link TypeInformation} does not implement the given type.
* @since 2.0
*/
default TypeInformation<?> getRequiredSuperTypeInformation(Class<?> superType) {
TypeInformation<?> result = getSuperTypeInformation(superType);
if (result == null) {
throw new IllegalArgumentException(String.format(
"Can't retrieve super type information for %s! Does current type really implement the given one?",
superType));
}
return result;
}
/**
* Returns if the current {@link TypeInformation} can be safely assigned to the given one. Mimics semantics of
* {@link Class#isAssignableFrom(Class)} but takes generics into account. Thus it will allow to detect that a

View File

@@ -22,6 +22,7 @@ import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -94,7 +95,7 @@ class TypeVariableTypeInformation<T> extends ParentTypeAwareTypeInformation<T> {
* @see org.springframework.data.util.ParentTypeAwareTypeInformation#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (obj == this) {
return true;

View File

@@ -18,6 +18,7 @@ package org.springframework.data.util;
import java.util.ArrayList;
import java.util.List;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -43,7 +44,8 @@ public class Version implements Comparable<Version> {
public Version(int... parts) {
Assert.notNull(parts, "Parts must not be null!");
Assert.isTrue(parts.length > 0 && parts.length < 5, String.format("Invalid parts length. 0 < %s < 5", parts.length));
Assert.isTrue(parts.length > 0 && parts.length < 5,
String.format("Invalid parts length. 0 < %s < 5", parts.length));
this.major = parts[0];
this.minor = parts.length > 1 ? parts[1] : 0;
@@ -139,11 +141,7 @@ public class Version implements Comparable<Version> {
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Version that) {
if (that == null) {
return 1;
}
public int compareTo(@SuppressWarnings("null") Version that) {
if (major != that.major) {
return major - that.major;
@@ -169,7 +167,7 @@ public class Version implements Comparable<Version> {
* @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,4 +1,5 @@
/**
* Core utility APIs such as a type information framework to resolve generic types.
*/
package org.springframework.data.util;
@org.springframework.lang.NonNullApi
package org.springframework.data.util;