DATACMNS-1101 - Remove Optional from TypeDiscoverer API.

This commit is contained in:
Mark Paluch
2017-06-29 13:11:10 +02:00
committed by Oliver Gierke
parent 2064c72a85
commit 0493d131e0
13 changed files with 312 additions and 329 deletions

View File

@@ -34,9 +34,10 @@ import org.springframework.util.StringUtils;
/**
* Abstraction of a {@link PropertyPath} of a domain class.
*
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
@EqualsAndHashCode
public class PropertyPath implements Streamable<PropertyPath> {
@@ -56,7 +57,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Creates a leaf {@link PropertyPath} (no nested ones) with the given name inside the given owning type.
*
*
* @param name must not be {@literal null} or empty.
* @param owningType must not be {@literal null}.
*/
@@ -66,7 +67,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Creates a leaf {@link PropertyPath} (no nested ones with the given name and owning type.
*
*
* @param name must not be {@literal null} or empty.
* @param owningType must not be {@literal null}.
* @param base the {@link PropertyPath} previously found.
@@ -78,8 +79,11 @@ public class PropertyPath implements Streamable<PropertyPath> {
Assert.notNull(base, "Perviously found properties must not be null!");
String propertyName = name.matches(ALL_UPPERCASE) ? name : StringUtils.uncapitalize(name);
TypeInformation<?> propertyType = owningType.getProperty(propertyName)
.orElseThrow(() -> new PropertyReferenceException(propertyName, owningType, base));
TypeInformation<?> propertyType = owningType.getProperty(propertyName);
if (propertyType == null) {
throw new PropertyReferenceException(propertyName, owningType, base);
}
this.owningType = owningType;
this.typeInformation = propertyType;
@@ -90,7 +94,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Returns the owning type of the {@link PropertyPath}.
*
*
* @return the owningType will never be {@literal null}.
*/
public TypeInformation<?> getOwningType() {
@@ -99,7 +103,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Returns the name of the {@link PropertyPath}.
*
*
* @return the name will never be {@literal null}.
*/
public String getSegment() {
@@ -108,7 +112,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Returns the leaf property of the {@link PropertyPath}.
*
*
* @return will never be {@literal null}.
*/
public PropertyPath getLeafProperty() {
@@ -125,7 +129,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Returns the type of the property will return the plain resolved type for simple properties, the component type for
* any {@link Iterable} or the value type of a {@link java.util.Map} if the property is one.
*
*
* @return
*/
public Class<?> getType() {
@@ -134,7 +138,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Returns the next nested {@link PropertyPath}.
*
*
* @return the next nested {@link PropertyPath} or {@literal null} if no nested {@link PropertyPath} available.
* @see #hasNext()
*/
@@ -145,7 +149,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Returns whether there is a nested {@link PropertyPath}. If this returns {@literal true} you can expect
* {@link #next()} to return a non- {@literal null} value.
*
*
* @return
*/
public boolean hasNext() {
@@ -154,7 +158,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Returns the {@link PropertyPath} in dot notation.
*
*
* @return
*/
public String toDotPath() {
@@ -168,14 +172,14 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Returns whether the {@link PropertyPath} is actually a collection.
*
*
* @return
*/
public boolean isCollection() {
return isCollection;
}
/*
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@@ -202,7 +206,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Extracts the {@link PropertyPath} chain from the given source {@link String} and type.
*
*
* @param source
* @param type
* @return
@@ -213,7 +217,8 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}. <br />
* Uses {@link #SPLITTER} by default and {@link #SPLITTER_FOR_QUOTED} for {@link Pattern#quote(String) quoted} literals.
* Uses {@link #SPLITTER} by default and {@link #SPLITTER_FOR_QUOTED} for {@link Pattern#quote(String) quoted}
* literals.
*
* @param source must not be {@literal null}.
* @param type
@@ -256,7 +261,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
/**
* Creates a new {@link PropertyPath} as subordinary of the given {@link PropertyPath}.
*
*
* @param source
* @param base
* @return
@@ -275,7 +280,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
* the given source for camel-case parts and traverse the {@link String} along its parts starting with the entire one
* and chewing off parts from the right side then. Whenever a valid property for the given class is found, the tail
* will be traversed for subordinary properties of the just found one and so on.
*
*
* @param source
* @param type
* @return
@@ -288,7 +293,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
* Tries to look up a chain of {@link PropertyPath}s by trying the givne source first. If that fails it will split the
* source apart at camel case borders (starting from the right side) and try to look up a {@link PropertyPath} from
* the calculated head and recombined new tail and additional tail.
*
*
* @param source
* @param type
* @param addTail
@@ -344,7 +349,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
throw exception;
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/

View File

@@ -268,7 +268,16 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
*/
@Override
public Class<?> getMapValueType() {
return isMap() ? information.getMapValueType().map(TypeInformation::getType).orElse(null) : null;
if (isMap()) {
TypeInformation<?> mapValueType = information.getMapValueType();
if (mapValueType != null) {
return mapValueType.getType();
}
}
return null;
}
/*

View File

@@ -37,11 +37,12 @@ import org.springframework.util.Assert;
/**
* Abstraction of a method that is designated to execute a finder query. Enriches the standard {@link Method} interface
* with specific information that is necessary to construct {@link RepositoryQuery}s for the method.
*
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Maciek Opała
* @author Mark Paluch
*/
public class QueryMethod {
@@ -55,7 +56,7 @@ public class QueryMethod {
/**
* Creates a new {@link QueryMethod} from the given parameters. Looks up the correct query to use for following
* invocations of the method given.
*
*
* @param method must not be {@literal null}.
* @param metadata must not be {@literal null}.
* @param factory must not be {@literal null}.
@@ -113,7 +114,7 @@ public class QueryMethod {
/**
* Creates a {@link Parameters} instance.
*
*
* @param method
* @return must not return {@literal null}.
*/
@@ -123,7 +124,7 @@ public class QueryMethod {
/**
* Returns the method's name.
*
*
* @return
*/
public String getName() {
@@ -137,7 +138,7 @@ public class QueryMethod {
/**
* Returns the name of the named query this method belongs to.
*
*
* @return
*/
public String getNamedQueryName() {
@@ -146,7 +147,7 @@ public class QueryMethod {
/**
* Returns the domain class the query method is targeted at.
*
*
* @return will never be {@literal null}.
*/
protected Class<?> getDomainClass() {
@@ -155,7 +156,7 @@ public class QueryMethod {
/**
* Returns the type of the object that will be returned.
*
*
* @return
*/
public Class<?> getReturnedObjectType() {
@@ -164,7 +165,7 @@ public class QueryMethod {
/**
* Returns whether the finder will actually return a collection of entities or a single one.
*
*
* @return
*/
public boolean isCollectionQuery() {
@@ -190,7 +191,7 @@ public class QueryMethod {
/**
* Returns whether the query method will return a {@link Slice}.
*
*
* @return
* @since 1.8
*/
@@ -200,7 +201,7 @@ public class QueryMethod {
/**
* Returns whether the finder will return a {@link Page} of results.
*
*
* @return
*/
public final boolean isPageQuery() {
@@ -209,7 +210,7 @@ public class QueryMethod {
/**
* Returns whether the query method is a modifying one.
*
*
* @return
*/
public boolean isModifyingQuery() {
@@ -218,7 +219,7 @@ public class QueryMethod {
/**
* Returns whether the query for this method actually returns entities.
*
*
* @return
*/
public boolean isQueryForEntity() {
@@ -227,7 +228,7 @@ public class QueryMethod {
/**
* Returns whether the method returns a Stream.
*
*
* @return
* @since 1.10
*/
@@ -237,7 +238,7 @@ public class QueryMethod {
/**
* Returns the {@link Parameters} wrapper to gain additional information about {@link Method} parameters.
*
*
* @return
*/
public Parameters<?, ?> getParameters() {
@@ -246,7 +247,7 @@ public class QueryMethod {
/**
* Returns the {@link ResultProcessor} to be used with the query method.
*
*
* @return the resultFactory
*/
public ResultProcessor getResultProcessor() {
@@ -265,10 +266,17 @@ public class QueryMethod {
private static Class<? extends Object> potentiallyUnwrapReturnTypeFor(Method method) {
if (QueryExecutionConverters.supports(method.getReturnType())) {
// unwrap only one level to handle cases like Future<List<Entity>> correctly.
return ClassTypeInformation.fromReturnTypeOf(method).getComponentType().map(TypeInformation::getType)
.orElseThrow(() -> new IllegalStateException(
String.format("Couldn't find component type for return value of method %s!", method)));
TypeInformation<?> componentType = ClassTypeInformation.fromReturnTypeOf(method).getComponentType();
if (componentType == null) {
throw new IllegalStateException(
String.format("Couldn't find component type for return value of method %s!", method));
}
return componentType.getType();
}
return method.getReturnType();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-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.
@@ -20,11 +20,10 @@ import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;
import java.util.Optional;
/**
* Special {@link TypeDiscoverer} handling {@link GenericArrayType}s.
*
*
* @author Oliver Gierke
*/
class GenericArrayTypeInformation<S> extends ParentTypeAwareTypeInformation<S> {
@@ -34,7 +33,7 @@ class GenericArrayTypeInformation<S> extends ParentTypeAwareTypeInformation<S> {
/**
* Creates a new {@link GenericArrayTypeInformation} for the given {@link GenericArrayTypeInformation} and
* {@link TypeDiscoverer}.
*
*
* @param type must not be {@literal null}.
* @param parent must not be {@literal null}.
* @param typeVariableMap must not be {@literal null}.
@@ -61,13 +60,13 @@ class GenericArrayTypeInformation<S> extends ParentTypeAwareTypeInformation<S> {
* @see org.springframework.data.util.TypeDiscoverer#doGetComponentType()
*/
@Override
protected Optional<TypeInformation<?>> doGetComponentType() {
protected TypeInformation<?> doGetComponentType() {
Type componentType = type.getGenericComponentType();
return Optional.of(createInfo(componentType));
return createInfo(componentType);
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/

View File

@@ -31,7 +31,7 @@ import org.springframework.util.StringUtils;
/**
* Base class for all types that include parameterization of some kind. Crucial as we have to take note of the parent
* class we will have to resolve generic parameters against.
*
*
* @author Oliver Gierke
* @author Mark Paluch
* @author Christoph Strobl
@@ -43,7 +43,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
/**
* Creates a new {@link ParameterizedTypeInformation} for the given {@link Type} and parent {@link TypeDiscoverer}.
*
*
* @param type must not be {@literal null}
* @param parent must not be {@literal null}
*/
@@ -59,14 +59,14 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
* @see org.springframework.data.util.TypeDiscoverer#doGetMapValueType()
*/
@Override
protected Optional<TypeInformation<?>> doGetMapValueType() {
protected TypeInformation<?> doGetMapValueType() {
if (Map.class.isAssignableFrom(getType())) {
Type[] arguments = type.getActualTypeArguments();
if (arguments.length > 1) {
return Optional.of(createInfo(arguments[1]));
return createInfo(arguments[1]);
}
}
@@ -79,14 +79,14 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
Optional<TypeInformation<?>> result = supertypes.stream()//
.map(it -> Pair.of(it, resolveType(it)))//
.filter(it -> Map.class.isAssignableFrom(it.getSecond()))//
.<TypeInformation<?>>map(it -> {
.<TypeInformation<?>> map(it -> {
ParameterizedType parameterizedSupertype = (ParameterizedType) it.getFirst();
Type[] arguments = parameterizedSupertype.getActualTypeArguments();
return createInfo(arguments[1]);
}).findFirst();
return result.isPresent() ? result : super.doGetMapValueType();
return result.orElseGet(super::doGetMapValueType);
}
/*
@@ -105,7 +105,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
return result;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeDiscoverer#isAssignableFrom(org.springframework.data.util.TypeInformation)
*/
@@ -147,11 +147,11 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
* @see org.springframework.data.util.TypeDiscoverer#doGetComponentType()
*/
@Override
protected Optional<TypeInformation<?>> doGetComponentType() {
return Optional.of(createInfo(type.getActualTypeArguments()[0]));
protected TypeInformation<?> doGetComponentType() {
return createInfo(type.getActualTypeArguments()[0]);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.util.ParentTypeAwareTypeInformation#equals(java.lang.Object)
*/
@@ -175,7 +175,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
return super.equals(obj);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.util.ParentTypeAwareTypeInformation#hashCode()
*/
@@ -184,7 +184,7 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
return isResolvedCompletely() ? this.type.hashCode() : super.hashCode();
}
/*
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/

View File

@@ -28,7 +28,16 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -43,6 +52,7 @@ import org.springframework.util.ReflectionUtils;
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
class TypeDiscoverer<S> implements TypeInformation<S> {
@@ -68,8 +78,8 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
private final int hashCode;
private final Lazy<Class<S>> resolvedType;
private final Lazy<Optional<TypeInformation<?>>> componentType;
private final Lazy<Optional<TypeInformation<?>>> valueType;
private final Lazy<TypeInformation<?>> componentType;
private final Lazy<TypeInformation<?>> valueType;
/**
* Creates a new {@link TypeDiscoverer} for the given type, type variable map and parent.
@@ -99,19 +109,17 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
return typeVariableMap;
}
private TypeInformation<?> createInfo(Optional<Type> fieldType) {
return fieldType.map(this::createInfo).orElseThrow(IllegalArgumentException::new);
}
/**
* Creates {@link TypeInformation} for the given {@link Type}.
*
* @param fieldType
* @param fieldType must not be {@literal null}.
* @return
*/
@SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
protected TypeInformation<?> createInfo(Type fieldType) {
Assert.notNull(fieldType, "Field type must not be null!");
if (fieldType.equals(this.type)) {
return this;
}
@@ -203,18 +211,22 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getProperty(java.lang.String)
*/
public Optional<TypeInformation<?>> getProperty(String fieldname) {
public TypeInformation<?> getProperty(String fieldname) {
int separatorIndex = fieldname.indexOf('.');
if (separatorIndex == -1) {
return fieldTypes.computeIfAbsent(fieldname, this::getPropertyInformation);
return fieldTypes.computeIfAbsent(fieldname, this::getPropertyInformation).orElse(null);
}
String head = fieldname.substring(0, separatorIndex);
Optional<TypeInformation<?>> info = getProperty(head);
TypeInformation<?> info = getProperty(head);
return info.map(it -> it.getProperty(fieldname.substring(separatorIndex + 1))).orElseGet(Optional::empty);
if (info == null) {
return null;
}
return info.getProperty(fieldname.substring(separatorIndex + 1));
}
/**
@@ -235,7 +247,6 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
}
return findPropertyDescriptor(rawType, fieldname).map(it -> createInfo(getGenericType(it)));
}
/**
@@ -269,22 +280,22 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @param descriptor must not be {@literal null}
* @return
*/
private static Optional<Type> getGenericType(PropertyDescriptor descriptor) {
private static Type getGenericType(PropertyDescriptor descriptor) {
Method method = descriptor.getReadMethod();
if (method != null) {
return Optional.of(method.getGenericReturnType());
return method.getGenericReturnType();
}
method = descriptor.getWriteMethod();
if (method == null) {
return Optional.empty();
return null;
}
Type[] parameterTypes = method.getGenericParameterTypes();
return Optional.ofNullable(parameterTypes.length == 0 ? null : parameterTypes[0]);
return parameterTypes.length == 0 ? null : parameterTypes[0];
}
/*
@@ -311,11 +322,11 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
public TypeInformation<?> getActualType() {
if (isMap()) {
return getMapValueType().orElse(null);
return getMapValueType();
}
if (isCollectionLike()) {
return getComponentType().orElse(null);
return getComponentType();
}
return this;
@@ -342,12 +353,13 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getMapValueType()
*/
public Optional<TypeInformation<?>> getMapValueType() {
public TypeInformation<?> getMapValueType() {
return valueType.get();
}
protected Optional<TypeInformation<?>> doGetMapValueType() {
return isMap() ? getTypeArgument(getBaseType(MAP_TYPES), 1) : getTypeArguments().stream().skip(1).findFirst();
protected TypeInformation<?> doGetMapValueType() {
return isMap() ? getTypeArgument(getBaseType(MAP_TYPES), 1)
: getTypeArguments().stream().skip(1).findFirst().orElse(null);
}
/*
@@ -365,16 +377,16 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getComponentType()
*/
public final Optional<TypeInformation<?>> getComponentType() {
public final TypeInformation<?> getComponentType() {
return componentType.get();
}
protected Optional<TypeInformation<?>> doGetComponentType() {
protected TypeInformation<?> doGetComponentType() {
Class<S> rawType = getType();
if (rawType.isArray()) {
return Optional.of(createInfo(rawType.getComponentType()));
return createInfo(rawType.getComponentType());
}
if (isMap()) {
@@ -387,7 +399,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
List<TypeInformation<?>> arguments = getTypeArguments();
return arguments.size() > 0 ? Optional.of(arguments.get(0)) : Optional.empty();
return arguments.size() > 0 ? arguments.get(0) : null;
}
/*
@@ -486,16 +498,16 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
: createInfo(new SyntheticParamterizedType(type, arguments)));
}
private Optional<TypeInformation<?>> getTypeArgument(Class<?> bound, int index) {
private TypeInformation<?> getTypeArgument(Class<?> bound, int index) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(getType(), bound);
if (arguments == null) {
return Optional.ofNullable(
getSuperTypeInformation(bound) instanceof ParameterizedTypeInformation ? ClassTypeInformation.OBJECT : null);
return getSuperTypeInformation(bound) instanceof ParameterizedTypeInformation ? ClassTypeInformation.OBJECT
: null;
}
return Optional.of(createInfo(arguments[index]));
return createInfo(arguments[index]);
}
private Class<?> getBaseType(Class<?>[] candidates) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2015 the original author or authors.
* Copyright 2008-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.
@@ -18,19 +18,19 @@ package org.springframework.data.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
/**
* 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.
*
*
* @author Oliver Gierke
* @author Mark Paluch
*/
public interface TypeInformation<S> {
/**
* Returns the {@link TypeInformation}s for the parameters of the given {@link Constructor}.
*
*
* @param constructor must not be {@literal null}.
* @return
*/
@@ -39,69 +39,106 @@ public interface TypeInformation<S> {
/**
* Returns the property information for the property with the given name. Supports property traversal through dot
* notation.
*
*
* @param property
* @return
*/
Optional<TypeInformation<?>> getProperty(String property);
TypeInformation<?> getProperty(String property);
/**
* Returns the property information for the property with the given name or throw {@link IllegalArgumentException} if
* the type information cannot be resolved. Supports property traversal through dot notation.
*
* @param property
* @return
* @throws IllegalArgumentException if the type information cannot be resolved.
* @since 2.0
*/
default TypeInformation<?> getRequiredProperty(String property) {
return getProperty(property).orElseThrow(() -> new IllegalArgumentException(
String.format("Could not find required property %s on %s!", property, getType())));
TypeInformation<?> typeInformation = getProperty(property);
if (typeInformation != null) {
return typeInformation;
}
throw new IllegalArgumentException(
String.format("Could not find required property %s on %s!", property, getType()));
}
/**
* Returns whether the type can be considered a collection, which means it's a container of elements, e.g. a
* {@link java.util.Collection} and {@link java.lang.reflect.Array} or anything implementing {@link Iterable}. If this
* returns {@literal true} you can expect {@link #getComponentType()} to return a non-{@literal null} value.
*
*
* @return
*/
boolean isCollectionLike();
/**
* Returns the component type for {@link java.util.Collection}s or the key type for {@link java.util.Map}s.
*
*
* @return
*/
Optional<TypeInformation<?>> getComponentType();
TypeInformation<?> getComponentType();
/**
* Returns the component type for {@link java.util.Collection}s, the key type for {@link java.util.Map}s or the single
* generic type if available throwing an exception if it can't be resolved.
*
* generic type if available. Throws {@link IllegalStateException} if the component value type cannot be resolved.
*
* @return
* @throws IllegalStateException if the component type cannot be resolved, e.g. if a raw type is used or the type is
* not generic in the first place.
* @since 2.0
*/
default TypeInformation<?> getRequiredComponentType() {
return getComponentType().orElseThrow(
() -> new IllegalStateException(String.format("Can't resolve required component type for %s!", getType())));
TypeInformation<?> componentType = getComponentType();
if (componentType != null) {
return getComponentType();
}
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
* {@link #getComponentType()} as well as {@link #getMapValueType()} to return something not {@literal null}.
*
*
* @return
*/
boolean isMap();
/**
* Will return the type of the value in case the underlying type is a {@link java.util.Map}.
*
*
* @return
*/
Optional<TypeInformation<?>> getMapValueType();
TypeInformation<?> getMapValueType();
/**
* Will return the type of the value in case the underlying type is a {@link java.util.Map} or throw
* {@link IllegalStateException} if the map value type cannot be resolved.
*
* @return
* @throws IllegalStateException if the map value type cannot be resolved.
* @since 2.0
*/
default TypeInformation<?> getRequiredMapValueType() {
return getMapValueType().orElseThrow(
() -> new IllegalStateException(String.format("Can't resolve required map value type for %s!", getType())));
TypeInformation<?> mapValueType = getMapValueType();
if (mapValueType != null) {
return mapValueType;
}
throw new IllegalStateException(String.format("Can't resolve required map value type for %s!", getType()));
}
/**
* Returns the type of the property. Will resolve generics and the generic context of
*
*
* @return
*/
Class<S> getType();
@@ -109,7 +146,7 @@ public interface TypeInformation<S> {
/**
* Returns a {@link ClassTypeInformation} to represent the {@link TypeInformation} of the raw type of the current
* instance.
*
*
* @return
*/
ClassTypeInformation<?> getRawTypeInformation();
@@ -117,7 +154,7 @@ 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
*/
TypeInformation<?> getActualType();
@@ -125,7 +162,7 @@ public interface TypeInformation<S> {
/**
* 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.
*
*
* @param method must not be {@literal null}.
* @return
*/
@@ -133,7 +170,7 @@ public interface TypeInformation<S> {
/**
* Returns the {@link TypeInformation}s for the parameters of the given {@link Method}.
*
*
* @param method must not be {@literal null}.
* @return
*/
@@ -141,7 +178,7 @@ public interface TypeInformation<S> {
/**
* 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 or {@literal null} in case the current
* {@link TypeInformation} does not implement the given type.
@@ -152,7 +189,7 @@ public interface TypeInformation<S> {
* 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
* {@code List<Long>} is assignable to {@code List<? extends Number>}.
*
*
* @param target
* @return
*/
@@ -160,7 +197,7 @@ public interface TypeInformation<S> {
/**
* Returns the {@link TypeInformation} for the type arguments of the current {@link TypeInformation}.
*
*
* @return
*/
List<TypeInformation<?>> getTypeArguments();
@@ -169,7 +206,7 @@ public interface TypeInformation<S> {
* Specializes the given (raw) {@link ClassTypeInformation} using the context of the current potentially parameterized
* type, basically turning the given raw type into a parameterized one. Will return the given type as is if no
* generics are involved.
*
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
*/

View File

@@ -34,6 +34,7 @@ import org.springframework.data.util.TypeInformation;
* Unit tests for {@link DefaultTypeMapper}.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class DefaultTypeMapperUnitTests {
@@ -81,8 +82,7 @@ public class DefaultTypeMapperUnitTests {
public void specializesRawSourceTypeUsingGenericContext() {
ClassTypeInformation<Foo> root = ClassTypeInformation.from(Foo.class);
TypeInformation<?> propertyType = root.getProperty("abstractBar")
.orElseThrow(() -> new IllegalStateException("Property abstractBar not found!"));
TypeInformation<?> propertyType = root.getProperty("abstractBar");
TypeInformation<?> barType = ClassTypeInformation.from(Bar.class);
doReturn(Alias.of(barType)).when(accessor).readAliasFrom(source);
@@ -95,8 +95,7 @@ public class DefaultTypeMapperUnitTests {
TypeInformation<?> typeInformation = TypeInformation.class.cast(result);
assertThat(typeInformation.getType()).isEqualTo(Bar.class);
assertThat(typeInformation.getProperty("field"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Character.class));
assertThat(typeInformation.getProperty("field").getType()).isEqualTo(Character.class);
}
static class TypeWithAbstractGenericType<T> {

View File

@@ -16,7 +16,7 @@
package org.springframework.data.util;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.util.ClassTypeInformation.from;
import static org.springframework.data.util.ClassTypeInformation.*;
import javaslang.collection.Traversable;
@@ -28,14 +28,14 @@ import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.assertj.core.api.OptionalAssert;
import org.junit.Test;
import org.springframework.data.mapping.Person;
/**
* Unit tests for {@link ClassTypeInformation}.
*
*
* @author Oliver Gierke
* @author Mark Paluch
*/
public class ClassTypeInformationUnitTests {
@@ -46,11 +46,11 @@ public class ClassTypeInformationUnitTests {
assertThat(discoverer.getType()).isEqualTo(ConcreteType.class);
OptionalAssert<TypeInformation<?>> assertThat = assertThat(discoverer.getProperty("content"));
TypeInformation<?> content = discoverer.getProperty("content");
assertThat.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat.flatMap(TypeInformation::getComponentType).isNotPresent();
assertThat.flatMap(TypeInformation::getMapValueType).isNotPresent();
assertThat(content.getType()).isEqualTo(String.class);
assertThat(content.getComponentType()).isNull();
assertThat(content.getMapValueType()).isNull();
}
@Test
@@ -59,14 +59,13 @@ public class ClassTypeInformationUnitTests {
TypeInformation<ConcreteWrapper> discoverer = ClassTypeInformation.from(ConcreteWrapper.class);
assertThat(discoverer.getType()).isEqualTo(ConcreteWrapper.class);
assertThat(discoverer.getProperty("wrapped")).hasValueSatisfying(it -> {
assertThat(discoverer.getProperty("wrapped")).satisfies(it -> {
assertThat(it.getType()).isEqualTo(GenericType.class);
assertThat(it.getProperty("content"))
.hasValueSatisfying(nested -> assertThat(nested.getType()).isEqualTo(String.class));
assertThat(it.getProperty("content")).satisfies(nested -> assertThat(nested.getType()).isEqualTo(String.class));
});
assertThat(discoverer.getProperty("wrapped.content"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
.satisfies(it -> assertThat(it.getType()).isEqualTo(String.class));
}
@Test
@@ -74,8 +73,7 @@ public class ClassTypeInformationUnitTests {
public void discoversBoundType() {
TypeInformation<GenericTypeWithBound> information = ClassTypeInformation.from(GenericTypeWithBound.class);
assertThat(information.getProperty("person"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Person.class));
assertThat(information.getProperty("person")).satisfies(it -> assertThat(it.getType()).isEqualTo(Person.class));
}
@Test
@@ -84,7 +82,7 @@ public class ClassTypeInformationUnitTests {
TypeInformation<SpecialGenericTypeWithBound> information = ClassTypeInformation
.from(SpecialGenericTypeWithBound.class);
assertThat(information.getProperty("person"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(SpecialPerson.class));
.satisfies(it -> assertThat(it.getType()).isEqualTo(SpecialPerson.class));
}
@Test
@@ -94,9 +92,9 @@ public class ClassTypeInformationUnitTests {
TypeInformation<AnotherGenericType> information = ClassTypeInformation.from(AnotherGenericType.class);
assertThat(information.getProperty("nested"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(GenericTypeWithBound.class));
.satisfies(it -> assertThat(it.getType()).isEqualTo(GenericTypeWithBound.class));
assertThat(information.getProperty("nested.person"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Person.class));
.satisfies(it -> assertThat(it.getType()).isEqualTo(Person.class));
}
@Test
@@ -104,47 +102,41 @@ public class ClassTypeInformationUnitTests {
TypeInformation<StringCollectionContainer> information = ClassTypeInformation.from(StringCollectionContainer.class);
OptionalAssert<TypeInformation<?>> optional = assertThat(information.getProperty("array"));
TypeInformation<?> array = information.getProperty("array");
optional.flatMap(TypeInformation::getComponentType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
optional.map(TypeInformation::getType).hasValueSatisfying(it -> {
assertThat(it).isEqualTo(String[].class);
assertThat(it.isArray()).isTrue();
});
assertThat(array.getComponentType().getType()).isEqualTo(String.class);
assertThat(array.getType()).isEqualTo(String[].class);
assertThat(array.getType().isArray()).isTrue();
optional = assertThat(information.getProperty("foo"));
TypeInformation<?> foo = information.getProperty("foo");
optional.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Collection[].class));
optional.flatMap(TypeInformation::getComponentType).hasValueSatisfying(it -> {
assertThat(foo.getType()).isEqualTo(Collection[].class);
assertThat(foo.getComponentType()).satisfies(it -> {
assertThat(it.getType()).isEqualTo(Collection.class);
assertThat(it.getComponentType())
.hasValueSatisfying(nested -> assertThat(nested.getType()).isEqualTo(String.class));
assertThat(it.getComponentType()).satisfies(nested -> assertThat(nested.getType()).isEqualTo(String.class));
});
optional = assertThat(information.getProperty("rawSet"));
TypeInformation<?> rawSet = information.getProperty("rawSet");
optional.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Set.class));
optional.flatMap(TypeInformation::getComponentType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Object.class));
optional.flatMap(TypeInformation::getMapValueType).isNotPresent();
assertThat(rawSet.getType()).isEqualTo(Set.class);
assertThat(rawSet.getComponentType().getType()).isEqualTo(Object.class);
assertThat(rawSet.getMapValueType()).isNull();
}
@Test
public void discoversMapValueType() {
TypeInformation<StringMapContainer> information = ClassTypeInformation.from(StringMapContainer.class);
OptionalAssert<TypeInformation<?>> assertion = assertThat(information.getProperty("genericMap"));
assertion.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Map.class));
assertion.flatMap(TypeInformation::getMapValueType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
TypeInformation<?> genericMap = information.getProperty("genericMap");
assertion = assertThat(information.getProperty("map"));
assertThat(genericMap.getType()).isEqualTo(Map.class);
assertThat(genericMap.getMapValueType().getType()).isEqualTo(String.class);
assertion.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Map.class));
assertion.flatMap(TypeInformation::getMapValueType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Calendar.class));
TypeInformation<?> map = information.getProperty("map");
assertThat(map.getType()).isEqualTo(Map.class);
assertThat(map.getMapValueType().getType()).isEqualTo(Calendar.class);
}
@Test
@@ -161,8 +153,8 @@ public class ClassTypeInformationUnitTests {
TypeInformation<PropertyGetter> from = ClassTypeInformation.from(PropertyGetter.class);
assertThat(from.getProperty("_name")).hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(from.getProperty("name")).hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(byte[].class));
assertThat(from.getProperty("_name")).satisfies(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(from.getProperty("name")).satisfies(it -> assertThat(it.getType()).isEqualTo(byte[].class));
}
@Test // DATACMNS-77
@@ -177,19 +169,17 @@ public class ClassTypeInformationUnitTests {
TypeInformation<ClassWithWildCardBound> information = ClassTypeInformation.from(ClassWithWildCardBound.class);
OptionalAssert<TypeInformation<?>> assertion = assertThat(information.getProperty("wildcard"));
TypeInformation<?> wildcard = information.getProperty("wildcard");
assertion.map(TypeInformation::isCollectionLike).hasValue(true);
assertion.flatMap(TypeInformation::getComponentType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(wildcard.isCollectionLike()).isTrue();
assertThat(wildcard.getComponentType().getType()).isEqualTo(String.class);
assertion = assertThat(information.getProperty("complexWildcard"));
TypeInformation<?> complexWildcard = information.getProperty("complexWildcard");
assertion.map(TypeInformation::isCollectionLike).hasValue(true);
assertion.flatMap(TypeInformation::getComponentType).hasValueSatisfying(it -> {
assertThat(complexWildcard.isCollectionLike()).isTrue();
assertThat(complexWildcard.getComponentType()).satisfies(it -> {
assertThat(it.isCollectionLike()).isEqualTo(true);
assertThat(it.getComponentType())
.hasValueSatisfying(nested -> assertThat(nested.getType()).isEqualTo(String.class));
assertThat(it.getComponentType()).satisfies(nested -> assertThat(nested.getType()).isEqualTo(String.class));
});
}
@@ -279,8 +269,7 @@ public class ClassTypeInformationUnitTests {
TypeInformation<?> information = from(String[][].class);
assertThat(information.getType()).isEqualTo(String[][].class);
assertThat(information.getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String[].class));
assertThat(information.getComponentType()).satisfies(it -> assertThat(it.getType()).isEqualTo(String[].class));
assertThat(information.getActualType().getActualType().getType()).isEqualTo(String.class);
}
@@ -289,7 +278,7 @@ public class ClassTypeInformationUnitTests {
TypeInformation<Product> information = from(Product.class);
assertThat(information.getProperty("category.id")).hasValue(from(Long.class));
assertThat(information.getProperty("category.id")).isEqualTo(from(Long.class));
}
@Test(expected = IllegalArgumentException.class) // DATACMNS-387
@@ -300,30 +289,28 @@ public class ClassTypeInformationUnitTests {
@Test // DATACMNS-422
public void returnsEmptyOptionalForRawTypesOnly() {
assertThat(from(MyRawIterable.class).getComponentType()).isNotPresent();
assertThat(from(MyIterable.class).getComponentType()).isPresent();
assertThat(from(MyRawIterable.class).getComponentType()).isNull();
assertThat(from(MyIterable.class).getComponentType()).isNotNull();
}
@Test // DATACMNS-440
public void detectsSpecialMapAsMapValueType() {
OptionalAssert<TypeInformation<?>> assertion = assertThat(
ClassTypeInformation.from(SuperGenerics.class).getProperty("seriously"));
TypeInformation<?> seriously = ClassTypeInformation.from(SuperGenerics.class).getProperty("seriously");
assertion//
// Type
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(SortedMap.class))//
// Type
assertThat(seriously.getType()).isEqualTo(SortedMap.class);
// Map value type
.flatMap(TypeInformation::getMapValueType).hasValueSatisfying(value -> {
assertThat(value.getType()).isEqualTo(SortedMap.class);
assertThat(value.getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
}).flatMap(TypeInformation::getMapValueType).hasValueSatisfying(nestedValue -> {
assertThat(nestedValue.getType()).isEqualTo(List.class);
assertThat(nestedValue.getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Person.class));
});
// Map value type
assertThat(seriously.getMapValueType()).satisfies(value -> {
assertThat(value.getType()).isEqualTo(SortedMap.class);
assertThat(value.getComponentType()).satisfies(it -> assertThat(it.getType()).isEqualTo(String.class));
});
assertThat(seriously.getMapValueType().getMapValueType()).satisfies(nestedValue -> {
assertThat(nestedValue.getType()).isEqualTo(List.class);
assertThat(nestedValue.getComponentType()).satisfies(it -> assertThat(it.getType()).isEqualTo(Person.class));
});
}
@Test // DATACMNS-446
@@ -337,18 +324,16 @@ public class ClassTypeInformationUnitTests {
ClassTypeInformation<ConcreteRoot> rootType = from(ConcreteRoot.class);
assertThat(rootType.getProperty("subs"))//
.map(TypeInformation::getActualType)//
.flatMap(it -> it.getProperty("subSub"))//
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(ConcreteSubSub.class));
assertThat(rootType.getProperty("subs").getActualType().getProperty("subSub").getType())//
.isEqualTo(ConcreteSubSub.class);
}
@Test // DATACMNS-594
public void considersGenericsOfTypeBounds() {
assertThat(ClassTypeInformation.from(ConcreteRootIntermediate.class)
.getProperty("intermediate.content.intermediate.content"))//
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Leaf.class));
.getProperty("intermediate.content.intermediate.content").getType())//
.isEqualTo(Leaf.class);
}
@Test // DATACMNS-783, DATACMNS-853
@@ -356,14 +341,11 @@ public class ClassTypeInformationUnitTests {
ClassTypeInformation<Foo> root = ClassTypeInformation.from(Foo.class);
assertThat(root.getProperty("abstractBar"))//
.map(it -> it.specialize(ClassTypeInformation.from(Bar.class)))//
.hasValueSatisfying(it -> {
assertThat(root.getProperty("abstractBar").specialize(ClassTypeInformation.from(Bar.class)))//
.satisfies(it -> {
assertThat(it.getType()).isEqualTo(Bar.class);
assertThat(it.getProperty("field"))
.hasValueSatisfying(nested -> assertThat(nested.getType()).isEqualTo(Character.class));
assertThat(it.getProperty("anotherField"))
.hasValueSatisfying(nested -> assertThat(nested.getType()).isEqualTo(Integer.class));
assertThat(it.getProperty("field").getType()).isEqualTo(Character.class);
assertThat(it.getProperty("anotherField").getType()).isEqualTo(Integer.class);
});
}
@@ -373,7 +355,7 @@ public class ClassTypeInformationUnitTests {
ClassTypeInformation<Foo> root = ClassTypeInformation.from(Foo.class);
ClassTypeInformation<?> from = ClassTypeInformation.from(Bar.class);
assertThat(root.getProperty("object")).hasValueSatisfying(it -> assertThat(it.specialize(from)).isEqualTo(from));
assertThat(root.getProperty("object").specialize(from)).isEqualTo(from);
}
@Test // DATACMNS-855
@@ -381,12 +363,10 @@ public class ClassTypeInformationUnitTests {
ClassTypeInformation<Foo> root = ClassTypeInformation.from(Foo.class);
OptionalAssert<TypeInformation<?>> assertion = assertThat(root.getProperty("abstractBar"));
TypeInformation<?> abstractBar = root.getProperty("abstractBar");
assertion
.map(it -> Pair.of(it.specialize(ClassTypeInformation.from(Bar.class)),
it.specialize(ClassTypeInformation.from(Bar.class))))//
.hasValueSatisfying(pair -> {
assertThat(Pair.of(abstractBar.specialize(ClassTypeInformation.from(Bar.class)),
abstractBar.specialize(ClassTypeInformation.from(Bar.class)))).satisfies(pair -> {
assertThat(pair.getFirst()).isEqualTo(pair.getSecond());
assertThat(pair.getSecond()).isEqualTo(pair.getFirst());
assertThat(pair.getFirst().hashCode()).isEqualTo(pair.getSecond().hashCode());
@@ -398,8 +378,7 @@ public class ClassTypeInformationUnitTests {
ClassTypeInformation<Concrete> information = ClassTypeInformation.from(Concrete.class);
assertThat(information.getProperty("field"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Nested.class));
assertThat(information.getProperty("field").getType()).isEqualTo(Nested.class);
}
@Test // DATACMNS-940
@@ -407,8 +386,7 @@ public class ClassTypeInformationUnitTests {
ClassTypeInformation<SampleTraversable> information = ClassTypeInformation.from(SampleTraversable.class);
assertThat(information.getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isAssignableFrom(Integer.class));
assertThat(information.getComponentType().getType()).isAssignableFrom(Integer.class);
}
@Test // DATACMNS-940
@@ -416,11 +394,9 @@ public class ClassTypeInformationUnitTests {
ClassTypeInformation<SampleMap> information = ClassTypeInformation.from(SampleMap.class);
assertThat(information.getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isAssignableFrom(String.class));
assertThat(information.getComponentType().getType()).isAssignableFrom(String.class);
assertThat(information.getMapValueType())
.hasValueSatisfying(it -> assertThat(it.getType()).isAssignableFrom(Integer.class));
assertThat(information.getMapValueType().getType()).isAssignableFrom(Integer.class);
}
static class StringMapContainer extends MapContainer<String> {

View File

@@ -18,36 +18,30 @@ package org.springframework.data.util;
import static org.assertj.core.api.Assertions.*;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import org.assertj.core.api.OptionalAssert;
import org.junit.Test;
/**
* Unit tests to reproduce issues reported in DATACMNS-511.
*
*
* @author Oliver Gierke
* @author Mark Paluch
*/
public class DataCmns511Tests {
@Test // DATACMNS-511
public void detectsEqualTypeVariableTypeInformationInstances() {
OptionalAssert<TypeInformation<?>> assertion = assertThat(
ClassTypeInformation.from(AbstractRole.class).getProperty("createdBy"));
TypeInformation<?> createdBy = ClassTypeInformation.from(AbstractRole.class).getProperty("createdBy");
assertion.flatMap(it -> it.getProperty("roles"))//
.map(TypeInformation::getActualType)//
.flatMap(it -> it.getProperty("createdBy"))//
.hasValueSatisfying(second -> {
assertThat(createdBy.getProperty("roles").getActualType().getProperty("createdBy"))//
.satisfies(second -> {
Optional<TypeInformation<?>> third = second.getProperty("roles")//
.map(TypeInformation::getActualType)//
.flatMap(it -> it.getProperty("createdBy"));
TypeInformation<?> third = second.getProperty("roles").getActualType().getProperty("createdBy");
assertThat(third).hasValue(second);
assertThat(third).map(Object::hashCode).hasValue(second.hashCode());
assertThat(third).isEqualTo(second);
assertThat(third.hashCode()).isEqualTo(second.hashCode());
});
}

View File

@@ -27,9 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.assertj.core.api.OptionalAssert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,7 +36,7 @@ import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit tests for {@link ParameterizedTypeInformation}.
*
*
* @author Oliver Gierke
* @author Mark Paluch
*/
@@ -81,48 +79,44 @@ public class ParameterizedTypeUnitTests {
public void resolvesMapValueTypeCorrectly() {
TypeInformation<Foo> type = ClassTypeInformation.from(Foo.class);
Optional<TypeInformation<?>> propertyType = type.getProperty("param");
TypeInformation<?> propertyType = type.getProperty("param");
TypeInformation<?> value = propertyType.getProperty("value");
OptionalAssert<TypeInformation<?>> assertion = assertThat(propertyType);
assertion.flatMap(it -> it.getProperty("value"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertion.flatMap(TypeInformation::getMapValueType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(value.getType()).isEqualTo(String.class);
assertThat(propertyType.getMapValueType().getType()).isEqualTo(String.class);
propertyType = type.getProperty("param2");
value = propertyType.getProperty("value");
assertion.flatMap(it -> it.getProperty("value"))
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertion.flatMap(TypeInformation::getMapValueType)
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(value.getType()).isEqualTo(String.class);
assertThat(propertyType.getMapValueType().getType()).isEqualTo(Locale.class);
}
@Test // DATACMNS-446
public void createsToStringRepresentation() {
assertThat(from(Foo.class).getProperty("param")).map(Object::toString)
.hasValue("org.springframework.data.util.ParameterizedTypeUnitTests$Localized<java.lang.String>");
assertThat(from(Foo.class).getProperty("param").toString())
.isEqualTo("org.springframework.data.util.ParameterizedTypeUnitTests$Localized<java.lang.String>");
}
@Test // DATACMNS-485
public void hashCodeShouldBeConsistentWithEqualsForResolvedTypes() {
Optional<TypeInformation<?>> first = from(First.class).getProperty("property");
Optional<TypeInformation<?>> second = from(Second.class).getProperty("property");
TypeInformation<?> first = from(First.class).getProperty("property");
TypeInformation<?> second = from(Second.class).getProperty("property");
assertThat(first).isEqualTo(second);
assertThat(first).hasValueSatisfying(left -> assertThat(second)
.hasValueSatisfying(right -> assertThat(left.hashCode()).isEqualTo(right.hashCode())));
assertThat(first).satisfies(
left -> assertThat(second).satisfies(right -> assertThat(left.hashCode()).isEqualTo(right.hashCode())));
}
@Test // DATACMNS-485
public void getActualTypeShouldNotUnwrapParameterizedTypes() {
Optional<TypeInformation<?>> type = from(First.class).getProperty("property");
TypeInformation<?> type = from(First.class).getProperty("property");
assertThat(type).map(TypeInformation::getActualType).isEqualTo(type);
assertThat(type.getActualType()).isEqualTo(type);
}
@Test // DATACMNS-697
@@ -130,20 +124,16 @@ public class ParameterizedTypeUnitTests {
TypeInformation<NormalizedProfile> information = ClassTypeInformation.from(NormalizedProfile.class);
assertThat(information.getProperty("education2.data"))//
.flatMap(TypeInformation::getComponentType)//
.flatMap(it -> it.getProperty("value"))//
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Education.class));
assertThat(information.getProperty("education2.data").getComponentType().getProperty("value"))//
.satisfies(it -> assertThat(it.getType()).isEqualTo(Education.class));
}
@Test // DATACMNS-899
public void returnsEmptyOptionalMapValueTypeForNonMapProperties() {
OptionalAssert<TypeInformation<?>> assertion = assertThat(
ClassTypeInformation.from(Bar.class).getProperty("param"));
assertion.hasValueSatisfying(it -> assertThat(it).isInstanceOf(ParameterizedTypeInformation.class));
assertion.flatMap(TypeInformation::getMapValueType).isEmpty();
TypeInformation<?> typeInformation = ClassTypeInformation.from(Bar.class).getProperty("param");
assertThat(typeInformation).isInstanceOf(ParameterizedTypeInformation.class);
assertThat(typeInformation.getMapValueType()).isNull();
}
@SuppressWarnings("serial")

View File

@@ -26,7 +26,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,7 +34,7 @@ import org.mockito.junit.MockitoJUnitRunner;
/**
* Unit tests for {@link TypeDiscoverer}.
*
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
@@ -75,9 +74,8 @@ public class TypeDiscovererUnitTests {
public void dealsWithTypesReferencingThemselves() {
TypeInformation<SelfReferencing> information = from(SelfReferencing.class);
Optional<TypeInformation<?>> first = information.getProperty("parent").flatMap(TypeInformation::getMapValueType);
Optional<TypeInformation<?>> second = first.flatMap(it -> it.getProperty("map"))
.flatMap(TypeInformation::getMapValueType);
TypeInformation<?> first = information.getProperty("parent").getMapValueType();
TypeInformation<?> second = first.getProperty("map").getMapValueType();
assertThat(second).isEqualTo(first);
}
@@ -86,9 +84,9 @@ public class TypeDiscovererUnitTests {
public void dealsWithTypesReferencingThemselvesInAMap() {
TypeInformation<SelfReferencingMap> information = from(SelfReferencingMap.class);
Optional<TypeInformation<?>> property = information.getProperty("map");
TypeInformation<?> property = information.getProperty("map");
assertThat(property).hasValueSatisfying(it -> assertThat(it.getMapValueType()).hasValue(information));
assertThat(property.getMapValueType()).isEqualTo(information);
}
@Test
@@ -96,10 +94,8 @@ public class TypeDiscovererUnitTests {
TypeInformation<?> discoverer = new TypeDiscoverer<>(CustomMap.class, EMPTY_MAP);
assertThat(discoverer.getMapValueType()).hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(Locale.class));
assertThat(discoverer.getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(discoverer.getMapValueType().getType()).isEqualTo(Locale.class);
assertThat(discoverer.getComponentType().getType()).isEqualTo(String.class);
}
@Test
@@ -107,8 +103,7 @@ public class TypeDiscovererUnitTests {
TypeDiscoverer<CustomCollection> discoverer = new TypeDiscoverer<>(CustomCollection.class, firstMap);
assertThat(discoverer.getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(discoverer.getComponentType().getType()).isEqualTo(String.class);
}
@Test
@@ -116,8 +111,7 @@ public class TypeDiscovererUnitTests {
TypeDiscoverer<String[]> discoverer = new TypeDiscoverer<>(String[].class, EMPTY_MAP);
assertThat(discoverer.getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(discoverer.getComponentType().getType()).isEqualTo(String.class);
}
@Test // DATACMNS-57
@@ -129,8 +123,7 @@ public class TypeDiscovererUnitTests {
assertThat(types).hasSize(2);
assertThat(types.get(0).getType()).isEqualTo(List.class);
assertThat(types.get(0).getComponentType())
.hasValueSatisfying(it -> assertThat(it.getType()).isEqualTo(String.class));
assertThat(types.get(0).getComponentType().getType()).isEqualTo(String.class);
}
@Test
@@ -139,8 +132,8 @@ public class TypeDiscovererUnitTests {
TypeDiscoverer<Map> discoverer = new TypeDiscoverer<>(Map.class, EMPTY_MAP);
assertThat(discoverer.getComponentType()).isEmpty();
assertThat(discoverer.getMapValueType()).isEmpty();
assertThat(discoverer.getComponentType()).isNull();
assertThat(discoverer.getMapValueType()).isNull();
}
@Test // DATACMNS-167
@@ -150,18 +143,18 @@ public class TypeDiscovererUnitTests {
TypeDiscoverer<Person> discoverer = new TypeDiscoverer<>(Person.class, EMPTY_MAP);
TypeInformation reference = from(Address.class);
Optional<TypeInformation<?>> addresses = discoverer.getProperty("addresses");
TypeInformation<?> addresses = discoverer.getProperty("addresses");
assertThat(addresses).hasValueSatisfying(it -> {
assertThat(addresses).satisfies(it -> {
assertThat(it.isCollectionLike()).isFalse();
assertThat(it.getComponentType()).hasValue(reference);
assertThat(it.getComponentType()).isEqualTo(reference);
});
Optional<TypeInformation<?>> adressIterable = discoverer.getProperty("addressIterable");
TypeInformation<?> adressIterable = discoverer.getProperty("addressIterable");
assertThat(adressIterable).hasValueSatisfying(it -> {
assertThat(adressIterable).satisfies(it -> {
assertThat(it.isCollectionLike()).isTrue();
assertThat(it.getComponentType()).hasValue(reference);
assertThat(it.getComponentType()).isEqualTo(reference);
});
}

View File

@@ -1,39 +0,0 @@
package org.springframework.data.util;
import java.util.Optional;
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractObjectAssert;
import org.assertj.core.api.Assertions;
public class TypeInformationAssert extends AbstractAssert<TypeInformationAssert, TypeInformation<?>> {
/**
* @param actual
* @param selfType
*/
public TypeInformationAssert(TypeInformation<?> actual) {
super(actual, TypeInformationAssert.class);
}
public static TypeInformationAssert assertThat(TypeInformation<?> information) {
return new TypeInformationAssert(information);
}
public TypeInformationAssert hasComponentType(Class<?> type) {
Assertions.assertThat(actual.getComponentType()).hasValueSatisfying(it -> Assertions.assertThat(it.getType()).isEqualTo(type));
return this;
}
public AbstractObjectAssert<?, TypeInformation<?>> hasProperty(String property) {
Optional<TypeInformation<?>> property2 = actual.getProperty(property);
return Assertions.assertThat(property2.orElseGet(() -> {
failWithMessage("Property %s not found!", property);
return null;
}));
}
}