DATACMNS-867 - Second draft.

This commit is contained in:
Oliver Gierke
2016-06-21 16:52:28 +02:00
parent 57ed50a730
commit d4811e29d9
222 changed files with 2297 additions and 2138 deletions

View File

@@ -1,107 +0,0 @@
/*
* Copyright 2014-2015 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.util;
/**
* Wrapper to safely store {@literal null} values in the value cache.
*
* @author Patryk Wasik
* @author Oliver Gierke
* @author Thomas Darimont
*/
public class CacheValue<T> {
private static final CacheValue<?> ABSENT = new CacheValue<Object>(null);
private final T value;
/**
* Creates a new {@link CacheValue} for the gven value.
*
* @param type can be {@literal null}.
*/
private CacheValue(T type) {
this.value = type;
}
/**
* Returns the actual underlying value.
*
* @return
*/
public T getValue() {
return value;
}
/**
* Returns whether the cached value has an actual value.
*
* @return
*/
public boolean isPresent() {
return value != null;
}
/**
* Returns whether the cached value has the given actual value.
*
* @param value can be {@literal null};
* @return
*/
public boolean hasValue(T value) {
return isPresent() ? this.value.equals(value) : value == null;
}
/**
* Returns a new {@link CacheValue} for the given value.
*
* @param value can be {@literal null}.
* @return will never be {@literal null}.
*/
@SuppressWarnings("unchecked")
public static <T> CacheValue<T> ofNullable(T value) {
return value == null ? (CacheValue<T>) ABSENT : new CacheValue<T>(value);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return isPresent() ? 0 : value.hashCode();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CacheValue)) {
return false;
}
CacheValue<?> that = (CacheValue<?>) obj;
return this.value == null ? false : this.value.equals(that.value);
}
}

View File

@@ -0,0 +1,12 @@
package org.springframework.data.util;
import lombok.experimental.UtilityClass;
@UtilityClass
public class CastUtils {
@SuppressWarnings("unchecked")
public <T> T cast(Object object) {
return (T) object;
}
}

View File

@@ -78,7 +78,7 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
return (ClassTypeInformation<S>) cachedTypeInfo;
}
ClassTypeInformation<S> result = new ClassTypeInformation<S>(type);
ClassTypeInformation<S> result = new ClassTypeInformation<>(type);
CACHE.put(type, new WeakReference<ClassTypeInformation<?>>(result));
return result;
}
@@ -113,7 +113,7 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
* @return
*/
private static Map<TypeVariable<?>, Type> getTypeVariableMap(Class<?> type) {
return getTypeVariableMap(type, new HashSet<Type>());
return getTypeVariableMap(type, new HashSet<>());
}
@SuppressWarnings("deprecation")
@@ -178,8 +178,8 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
* @see org.springframework.data.util.TypeDiscoverer#specialize(org.springframework.data.util.ClassTypeInformation)
*/
@Override
public TypeInformation<?> specialize(ClassTypeInformation<?> type) {
return type;
public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
return (TypeInformation<? extends S>) type;
}
/*

View File

@@ -20,6 +20,7 @@ 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.
@@ -60,10 +61,10 @@ class GenericArrayTypeInformation<S> extends ParentTypeAwareTypeInformation<S> {
* @see org.springframework.data.util.TypeDiscoverer#doGetComponentType()
*/
@Override
protected TypeInformation<?> doGetComponentType() {
protected Optional<TypeInformation<?>> doGetComponentType() {
Type componentType = type.getGenericComponentType();
return createInfo(componentType);
return Optional.of(createInfo(componentType));
}
/*

View File

@@ -22,7 +22,11 @@ import java.util.Optional;
import java.util.function.Supplier;
/**
* Simple value type to delay the creation of an object using a {@link Supplier} returning the produced object for
* subsequent lookups.
*
* @author Oliver Gierke
* @since 2.0
*/
@RequiredArgsConstructor
@EqualsAndHashCode
@@ -31,12 +35,20 @@ public class Lazy<T> {
private final Supplier<T> supplier;
private Optional<T> value;
/**
* Creates a new {@link Lazy} to produce an object lazily.
*
* @param <T> the type of which to produce an object of eventually.
* @param supplier the {@link Supplier} to create the object lazily.
* @return
*/
public static <T> Lazy<T> of(Supplier<T> supplier) {
return new Lazy<T>(supplier);
return new Lazy<>(supplier);
}
/**
* Returns the value created by the configured {@link Supplier}.
* Returns the value created by the configured {@link Supplier}. Will return the calculated instance for subsequent
* lookups.
*
* @return
*/

View File

@@ -20,6 +20,7 @@ import lombok.experimental.UtilityClass;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import org.springframework.util.Assert;
@@ -56,10 +57,68 @@ public class Optionals {
Assert.notNull(optionals, "Optional must not be null!");
return Arrays.asList(optionals).stream().flatMap(it -> it.map(Stream::of).orElse(Stream.empty()));
return Arrays.asList(optionals).stream().flatMap(it -> it.map(Stream::of).orElseGet(() -> Stream.empty()));
}
/**
* Applies the given function to the elements of the source and returns the first non-empty result.
*
* @param source must not be {@literal null}.
* @param function must not be {@literal null}.
* @return
*/
public static <S, T> Optional<T> firstNonEmpty(Iterable<S> source, Function<S, Optional<T>> function) {
Assert.notNull(source, "Source must not be null!");
Assert.notNull(function, "Function must not be null!");
return Streamable.of(source).stream()//
.map(it -> function.apply(it))//
.filter(it -> it.isPresent())//
.findFirst().orElseGet(() -> Optional.empty());
}
/**
* Applies the given function to the elements of the source and returns the first non-empty result.
*
* @param source must not be {@literal null}.
* @param function must not be {@literal null}.
* @return
*/
public static <S, T> T firstNonEmpty(Iterable<S> source, Function<S, T> function, T defaultValue) {
Assert.notNull(source, "Source must not be null!");
Assert.notNull(function, "Function must not be null!");
return Streamable.of(source).stream()//
.map(it -> function.apply(it))//
.filter(it -> !it.equals(defaultValue))//
.findFirst().orElse(defaultValue);
}
/**
* Returns the next element of the given {@link Iterator} or {@link Optional#empty()} in case there is no next
* element.
*
* @param iterator must not be {@literal null}.
* @return
*/
public static <T> Optional<T> next(Iterator<T> iterator) {
Assert.notNull(iterator, "Iterator must not be null!");
return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.empty();
}
/**
* Returns a {@link Pair} if both {@link Optional} instances have values or {@link Optional#empty()} if one or both
* are missing.
*
* @param left
* @param right
* @return
*/
public static <T, S> Optional<Pair<T, S>> withBoth(Optional<T> left, Optional<S> right) {
return left.flatMap(l -> right.map(r -> Pair.of(l, r)));
}
}

View File

@@ -23,6 +23,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.springframework.util.StringUtils;
@@ -57,36 +58,34 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
* @see org.springframework.data.util.TypeDiscoverer#doGetMapValueType()
*/
@Override
protected TypeInformation<?> doGetMapValueType() {
protected Optional<TypeInformation<?>> doGetMapValueType() {
if (Map.class.isAssignableFrom(getType())) {
Type[] arguments = type.getActualTypeArguments();
if (arguments.length > 1) {
return createInfo(arguments[1]);
return Optional.of(createInfo(arguments[1]));
}
}
Class<?> rawType = getType();
Set<Type> supertypes = new HashSet<Type>();
supertypes.add(rawType.getGenericSuperclass());
Set<Type> supertypes = new HashSet<>();
Optional.ofNullable(rawType.getGenericSuperclass()).ifPresent(it -> supertypes.add(it));
supertypes.addAll(Arrays.asList(rawType.getGenericInterfaces()));
for (Type supertype : supertypes) {
Optional<TypeInformation<?>> result = supertypes.stream()//
.map(it -> Pair.of(it, resolveType(it)))//
.filter(it -> Map.class.isAssignableFrom(it.getSecond()))//
.<TypeInformation<?>>map(it -> {
Class<?> rawSuperType = resolveType(supertype);
ParameterizedType parameterizedSupertype = (ParameterizedType) it.getFirst();
Type[] arguments = parameterizedSupertype.getActualTypeArguments();
return createInfo(arguments[1]);
}).findFirst();
if (Map.class.isAssignableFrom(rawSuperType)) {
ParameterizedType parameterizedSupertype = (ParameterizedType) supertype;
Type[] arguments = parameterizedSupertype.getActualTypeArguments();
return createInfo(arguments[1]);
}
}
return super.doGetMapValueType();
return result.isPresent() ? result : super.doGetMapValueType();
}
/*
@@ -123,8 +122,8 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
return false;
}
TypeInformation<?> otherTypeInformation = rawType.equals(rawTargetType) ? target : target
.getSuperTypeInformation(rawType);
TypeInformation<?> otherTypeInformation = rawType.equals(rawTargetType) ? target
: target.getSuperTypeInformation(rawType);
List<TypeInformation<?>> myParameters = getTypeArguments();
List<TypeInformation<?>> typeParameters = otherTypeInformation.getTypeArguments();
@@ -147,8 +146,8 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
* @see org.springframework.data.util.TypeDiscoverer#doGetComponentType()
*/
@Override
protected TypeInformation<?> doGetComponentType() {
return createInfo(type.getActualTypeArguments()[0]);
protected Optional<TypeInformation<?>> doGetComponentType() {
return Optional.of(createInfo(type.getActualTypeArguments()[0]));
}
/*

View File

@@ -81,7 +81,7 @@ public abstract class ParsingUtils {
Assert.notNull(source, "Source string must not be null!");
String[] parts = CAMEL_CASE.split(source);
List<String> result = new ArrayList<String>(parts.length);
List<String> result = new ArrayList<>(parts.length);
for (String part : parts) {
result.add(toLower ? part.toLowerCase() : part);

View File

@@ -24,10 +24,14 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -271,6 +275,17 @@ public abstract class ReflectionUtils {
return Stream.concat(returnType, parameterTypes);
}
public static Optional<Method> getMethod(Class<?> type, String name, ResolvableType... parameterTypes) {
List<Class<?>> collect = Arrays.stream(parameterTypes).map(it -> it.getRawClass()).collect(Collectors.toList());
Optional<Method> method = Optional.ofNullable(
org.springframework.util.ReflectionUtils.findMethod(type, name, collect.toArray(new Class<?>[collect.size()])));
return method.filter(it -> IntStream.range(0, it.getParameterCount())//
.allMatch(index -> ResolvableType.forMethodParameter(it, index).equals(parameterTypes[index])));
}
private static final boolean argumentsMatch(Class<?>[] parameterTypes, Object[] arguments) {
if (parameterTypes.length != arguments.length) {

View File

@@ -38,10 +38,21 @@ public interface Streamable<T> extends Iterable<T> {
return StreamSupport.stream(spliterator(), false);
}
/**
* Returns an empty {@link Streamable}.
*
* @return will never be {@literal null}.
*/
public static <T> Streamable<T> empty() {
return () -> Collections.emptyIterator();
}
/**
* Returns a {@link Streamable} with the given elements.
*
* @param t the elements to return.
* @return
*/
@SafeVarargs
public static <T> Streamable<T> of(T... t) {
return () -> Arrays.asList(t).iterator();

View File

@@ -15,11 +15,9 @@
*/
package org.springframework.data.util;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
@@ -38,8 +36,10 @@ 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;
import org.springframework.beans.BeanUtils;
import org.springframework.core.GenericTypeResolver;
@@ -72,19 +72,15 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
private final Type type;
private final Map<TypeVariable<?>, Type> typeVariableMap;
private final Map<String, ValueHolder> fieldTypes = new ConcurrentHashMap<String, ValueHolder>();
private final Map<String, Optional<TypeInformation<?>>> fieldTypes = new ConcurrentHashMap<>();
private final int hashCode;
private boolean componentTypeResolved = false;
private TypeInformation<?> componentType;
private boolean valueTypeResolved = false;
private TypeInformation<?> valueType;
private Class<S> resolvedType;
private final Lazy<Class<S>> resolvedType;
private final Lazy<Optional<TypeInformation<?>>> componentType;
private final Lazy<Optional<TypeInformation<?>>> valueType;
/**
* Creates a ne {@link TypeDiscoverer} for the given type, type variable map and parent.
* Creates a new {@link TypeDiscoverer} for the given type, type variable map and parent.
*
* @param type must not be {@literal null}.
* @param typeVariableMap must not be {@literal null}.
@@ -95,6 +91,9 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
Assert.notNull(typeVariableMap, "TypeVariableMap must not be null!");
this.type = type;
this.resolvedType = Lazy.of(() -> resolveType(type));
this.componentType = Lazy.of(() -> doGetComponentType());
this.valueType = Lazy.of(() -> doGetMapValueType());
this.typeVariableMap = typeVariableMap;
this.hashCode = 17 + (31 * type.hashCode()) + (31 * typeVariableMap.hashCode());
}
@@ -108,6 +107,10 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
return typeVariableMap;
}
private TypeInformation<?> createInfo(Optional<Type> fieldType) {
return fieldType.map(it -> createInfo(it)).orElseThrow(() -> new IllegalArgumentException());
}
/**
* Creates {@link TypeInformation} for the given {@link Type}.
*
@@ -208,25 +211,18 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getProperty(java.lang.String)
*/
public TypeInformation<?> getProperty(String fieldname) {
public Optional<TypeInformation<?>> getProperty(String fieldname) {
int separatorIndex = fieldname.indexOf('.');
if (separatorIndex == -1) {
if (fieldTypes.containsKey(fieldname)) {
return fieldTypes.get(fieldname).getType();
}
TypeInformation<?> propertyInformation = getPropertyInformation(fieldname);
fieldTypes.put(fieldname, ValueHolder.of(propertyInformation));
return propertyInformation;
return fieldTypes.computeIfAbsent(fieldname, it -> getPropertyInformation(it));
}
String head = fieldname.substring(0, separatorIndex);
TypeInformation<?> info = getProperty(head);
return info == null ? null : info.getProperty(fieldname.substring(separatorIndex + 1));
Optional<TypeInformation<?>> info = getProperty(head);
return info.map(it -> it.getProperty(fieldname.substring(separatorIndex + 1))).orElseGet(() -> Optional.empty());
}
/**
@@ -237,17 +233,17 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @param fieldname
* @return
*/
private TypeInformation<?> getPropertyInformation(String fieldname) {
private Optional<TypeInformation<?>> getPropertyInformation(String fieldname) {
Class<?> rawType = getType();
Field field = ReflectionUtils.findField(rawType, fieldname);
if (field != null) {
return createInfo(field.getGenericType());
return Optional.of(createInfo(field.getGenericType()));
}
PropertyDescriptor descriptor = findPropertyDescriptor(rawType, fieldname);
return descriptor == null ? null : createInfo(getGenericType(descriptor));
return findPropertyDescriptor(rawType, fieldname).map(it -> createInfo(getGenericType(it)));
}
/**
@@ -257,26 +253,21 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @param fieldname must not be {@literal null} or empty.
* @return
*/
private static PropertyDescriptor findPropertyDescriptor(Class<?> type, String fieldname) {
private static Optional<PropertyDescriptor> findPropertyDescriptor(Class<?> type, String fieldname) {
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(type, fieldname);
if (descriptor != null) {
return descriptor;
return Optional.of(descriptor);
}
List<Class<?>> superTypes = new ArrayList<Class<?>>();
superTypes.addAll(Arrays.asList(type.getInterfaces()));
superTypes.add(type.getSuperclass());
for (Class<?> interfaceType : type.getInterfaces()) {
descriptor = findPropertyDescriptor(interfaceType, fieldname);
if (descriptor != null) {
return descriptor;
}
}
return null;
return Streamable.of(type.getInterfaces()).stream()//
.flatMap(it -> Optionals.toStream(findPropertyDescriptor(it, fieldname)))//
.findFirst();
}
/**
@@ -286,22 +277,22 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @param descriptor must not be {@literal null}
* @return
*/
private static Type getGenericType(PropertyDescriptor descriptor) {
private static Optional<Type> getGenericType(PropertyDescriptor descriptor) {
Method method = descriptor.getReadMethod();
if (method != null) {
return method.getGenericReturnType();
return Optional.of(method.getGenericReturnType());
}
method = descriptor.getWriteMethod();
if (method == null) {
return null;
return Optional.empty();
}
Type[] parameterTypes = method.getGenericParameterTypes();
return parameterTypes.length == 0 ? null : parameterTypes[0];
return Optional.ofNullable(parameterTypes.length == 0 ? null : parameterTypes[0]);
}
/*
@@ -309,12 +300,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @see org.springframework.data.util.TypeInformation#getType()
*/
public Class<S> getType() {
if (resolvedType == null) {
this.resolvedType = resolveType(type);
}
return this.resolvedType;
return resolvedType.get();
}
/*
@@ -333,11 +319,11 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
public TypeInformation<?> getActualType() {
if (isMap()) {
return getMapValueType();
return getMapValueType().orElse(null);
}
if (isCollectionLike()) {
return getComponentType();
return getComponentType().orElse(null);
}
return this;
@@ -362,29 +348,12 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getMapValueType()
*/
public TypeInformation<?> getMapValueType() {
if (!valueTypeResolved) {
this.valueType = doGetMapValueType();
this.valueTypeResolved = true;
}
return this.valueType;
public Optional<TypeInformation<?>> getMapValueType() {
return valueType.get();
}
protected TypeInformation<?> doGetMapValueType() {
if (isMap()) {
return getTypeArgument(getBaseType(MAP_TYPES), 1);
}
List<TypeInformation<?>> arguments = getTypeArguments();
if (arguments.size() > 1) {
return arguments.get(1);
}
return null;
protected Optional<TypeInformation<?>> doGetMapValueType() {
return isMap() ? getTypeArgument(getBaseType(MAP_TYPES), 1) : getTypeArguments().stream().skip(1).findFirst();
}
/*
@@ -395,33 +364,23 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
Class<?> rawType = getType();
if (rawType.isArray() || Iterable.class.equals(rawType)) {
return true;
}
return Collection.class.isAssignableFrom(rawType);
return rawType.isArray() || Iterable.class.equals(rawType) || Collection.class.isAssignableFrom(rawType);
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#getComponentType()
*/
public final TypeInformation<?> getComponentType() {
if (!componentTypeResolved) {
this.componentType = doGetComponentType();
this.componentTypeResolved = true;
}
return this.componentType;
public final Optional<TypeInformation<?>> getComponentType() {
return componentType.get();
}
protected TypeInformation<?> doGetComponentType() {
protected Optional<TypeInformation<?>> doGetComponentType() {
Class<S> rawType = getType();
if (rawType.isArray()) {
return createInfo(rawType.getComponentType());
return Optional.of(createInfo(rawType.getComponentType()));
}
if (isMap()) {
@@ -434,11 +393,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
List<TypeInformation<?>> arguments = getTypeArguments();
if (arguments.size() > 0) {
return arguments.get(0);
}
return null;
return arguments.size() > 0 ? Optional.of(arguments.get(0)) : Optional.empty();
}
/*
@@ -459,14 +414,9 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
Assert.notNull(method, "Method most not be null!");
Type[] types = method.getGenericParameterTypes();
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>(types.length);
for (Type parameterType : types) {
result.add(createInfo(parameterType));
}
return result;
return Streamable.of(method.getGenericParameterTypes()).stream()//
.map(it -> createInfo(it))//
.collect(Collectors.toList());
}
/*
@@ -485,7 +435,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
return this;
}
List<Type> candidates = new ArrayList<Type>();
List<Type> candidates = new ArrayList<>();
Type genericSuperclass = rawType.getGenericSuperclass();
if (genericSuperclass != null) {
@@ -530,25 +480,27 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
* @see org.springframework.data.util.TypeInformation#specialize(org.springframework.data.util.ClassTypeInformation)
*/
@Override
public TypeInformation<?> specialize(ClassTypeInformation<?> type) {
@SuppressWarnings("unchecked")
public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
Assert.isTrue(getType().isAssignableFrom(type.getType()), String.format("%s must be assignable from %s", getType(), type.getType()));
List<TypeInformation<?>> arguments = getTypeArguments();
return arguments.isEmpty() ? type : createInfo(new SyntheticParamterizedType(type, arguments));
return (TypeInformation<? extends S>) (arguments.isEmpty() ? type
: createInfo(new SyntheticParamterizedType(type, arguments)));
}
private TypeInformation<?> getTypeArgument(Class<?> bound, int index) {
private Optional<TypeInformation<?>> getTypeArgument(Class<?> bound, int index) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(getType(), bound);
if (arguments == null) {
return getSuperTypeInformation(bound) instanceof ParameterizedTypeInformation ? ClassTypeInformation.OBJECT
: null;
return Optional.ofNullable(
getSuperTypeInformation(bound) instanceof ParameterizedTypeInformation ? ClassTypeInformation.OBJECT : null);
}
return createInfo(arguments[index]);
return Optional.of(createInfo(arguments[index]));
}
private Class<?> getBaseType(Iterable<Class<?>> candidates) {
@@ -642,22 +594,4 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
return result;
}
}
/**
* Simple wrapper to be able to store {@literal null} values in a {@link ConcurrentHashMap}.
*
* @author Oliver Gierke
*/
@Value
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
private static class ValueHolder {
static ValueHolder NULL_HOLDER = new ValueHolder(null);
TypeInformation<?> type;
public static ValueHolder of(TypeInformation<?> type) {
return null == type ? NULL_HOLDER : new ValueHolder(type);
}
}
}

View File

@@ -18,6 +18,7 @@ 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}
@@ -36,13 +37,18 @@ public interface TypeInformation<S> {
List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor);
/**
* Returns the property information for the property with the given name. Supports proeprty traversal through dot
* Returns the property information for the property with the given name. Supports property traversal through dot
* notation.
*
* @param fieldname
* @param property
* @return
*/
TypeInformation<?> getProperty(String fieldname);
Optional<TypeInformation<?>> getProperty(String property);
default TypeInformation<?> getRequiredProperty(String property) {
return getProperty(property).orElseThrow(() -> 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
@@ -58,7 +64,20 @@ public interface TypeInformation<S> {
*
* @return
*/
TypeInformation<?> getComponentType();
Optional<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.
*
* @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.
*/
default TypeInformation<?> getRequiredComponentType() {
return getComponentType().orElseThrow(
() -> 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
@@ -73,7 +92,12 @@ public interface TypeInformation<S> {
*
* @return
*/
TypeInformation<?> getMapValueType();
Optional<TypeInformation<?>> getMapValueType();
default TypeInformation<?> getRequiredMapValueType() {
return getMapValueType().orElseThrow(
() -> 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
@@ -149,5 +173,5 @@ public interface TypeInformation<S> {
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
*/
TypeInformation<?> specialize(ClassTypeInformation<?> type);
TypeInformation<? extends S> specialize(ClassTypeInformation<?> type);
}

View File

@@ -207,7 +207,7 @@ public class Version implements Comparable<Version> {
@Override
public String toString() {
List<Integer> digits = new ArrayList<Integer>();
List<Integer> digits = new ArrayList<>();
digits.add(major);
digits.add(minor);