DependencyDescriptor supports TypeDescriptor resolution for fields
This allows for proper nested type conversion in @Value Optional fields analogous to method parameters, through a new TypeDescriptor-based method in the TypeConverter SPI. As an additional and less involved measure that is worth backporting, DefaultListableBeanFactory defensively checks for pre-converted Optional wrappers. Issue: SPR-17607
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.beans;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -93,4 +94,27 @@ public interface TypeConverter {
|
||||
<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
|
||||
throws TypeMismatchException;
|
||||
|
||||
/**
|
||||
* Convert the value to the required type (if necessary from a String).
|
||||
* <p>Conversions from String to any type will typically use the {@code setAsText}
|
||||
* method of the PropertyEditor class, or a Spring Converter in a ConversionService.
|
||||
* @param value the value to convert
|
||||
* @param requiredType the type we must convert to
|
||||
* (or {@code null} if not known, for example in case of a collection element)
|
||||
* @param typeDescriptor the type descriptor to use (may be {@code null}))
|
||||
* @return the new value, possibly the result of type conversion
|
||||
* @throws TypeMismatchException if type conversion failed
|
||||
* @since 5.1.4
|
||||
* @see java.beans.PropertyEditor#setAsText(String)
|
||||
* @see java.beans.PropertyEditor#getValue()
|
||||
* @see org.springframework.core.convert.ConversionService
|
||||
* @see org.springframework.core.convert.converter.Converter
|
||||
*/
|
||||
@Nullable
|
||||
default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
|
||||
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
|
||||
|
||||
throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -30,7 +30,6 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -82,42 +81,6 @@ class TypeConverterDelegate {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the value to the specified required type.
|
||||
* @param newValue the proposed new value
|
||||
* @param requiredType the type we must convert to
|
||||
* (or {@code null} if not known, for example in case of a collection element)
|
||||
* @param methodParam the method parameter that is the target of the conversion
|
||||
* (may be {@code null})
|
||||
* @return the new value, possibly the result of type conversion
|
||||
* @throws IllegalArgumentException if type conversion failed
|
||||
*/
|
||||
@Nullable
|
||||
public <T> T convertIfNecessary(@Nullable Object newValue, @Nullable Class<T> requiredType,
|
||||
@Nullable MethodParameter methodParam) throws IllegalArgumentException {
|
||||
|
||||
return convertIfNecessary(null, null, newValue, requiredType,
|
||||
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the value to the specified required type.
|
||||
* @param newValue the proposed new value
|
||||
* @param requiredType the type we must convert to
|
||||
* (or {@code null} if not known, for example in case of a collection element)
|
||||
* @param field the reflective field that is the target of the conversion
|
||||
* (may be {@code null})
|
||||
* @return the new value, possibly the result of type conversion
|
||||
* @throws IllegalArgumentException if type conversion failed
|
||||
*/
|
||||
@Nullable
|
||||
public <T> T convertIfNecessary(@Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable Field field)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
return convertIfNecessary(null, null, newValue, requiredType,
|
||||
(field != null ? new TypeDescriptor(field) : TypeDescriptor.valueOf(requiredType)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the value to the required type for the specified property.
|
||||
* @param propertyName name of the property
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -21,6 +21,7 @@ import java.lang.reflect.Field;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.ConverterNotFoundException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -41,15 +42,16 @@ public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException {
|
||||
return doConvert(value, requiredType, null, null);
|
||||
return convertIfNecessary(value, requiredType, TypeDescriptor.valueOf(requiredType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable MethodParameter methodParam)
|
||||
throws TypeMismatchException {
|
||||
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
|
||||
@Nullable MethodParameter methodParam) throws TypeMismatchException {
|
||||
|
||||
return doConvert(value, requiredType, methodParam, null);
|
||||
return convertIfNecessary(value, requiredType,
|
||||
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,21 +59,18 @@ public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport
|
||||
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
|
||||
throws TypeMismatchException {
|
||||
|
||||
return doConvert(value, requiredType, null, field);
|
||||
return convertIfNecessary(value, requiredType,
|
||||
(field != null ? new TypeDescriptor(field) : TypeDescriptor.valueOf(requiredType)));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T doConvert(@Nullable Object value,@Nullable Class<T> requiredType,
|
||||
@Nullable MethodParameter methodParam, @Nullable Field field) throws TypeMismatchException {
|
||||
@Override
|
||||
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
|
||||
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
|
||||
|
||||
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
|
||||
try {
|
||||
if (field != null) {
|
||||
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field);
|
||||
}
|
||||
else {
|
||||
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
|
||||
}
|
||||
return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
|
||||
}
|
||||
catch (ConverterNotFoundException | IllegalStateException ex) {
|
||||
throw new ConversionNotSupportedException(value, requiredType, ex);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -77,6 +78,9 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
||||
@Nullable
|
||||
private transient volatile ResolvableType resolvableType;
|
||||
|
||||
@Nullable
|
||||
private transient volatile TypeDescriptor typeDescriptor;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new descriptor for a method or constructor parameter.
|
||||
@@ -301,7 +305,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a ResolvableType object for the wrapped parameter/field.
|
||||
* Build a {@link ResolvableType} object for the wrapped parameter/field.
|
||||
* @since 4.0
|
||||
*/
|
||||
public ResolvableType getResolvableType() {
|
||||
@@ -315,6 +319,21 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
||||
return resolvableType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link TypeDescriptor} object for the wrapped parameter/field.
|
||||
* @since 5.1.4
|
||||
*/
|
||||
public TypeDescriptor getTypeDescriptor() {
|
||||
TypeDescriptor typeDescriptor = this.typeDescriptor;
|
||||
if (typeDescriptor == null) {
|
||||
typeDescriptor = (this.field != null ?
|
||||
new TypeDescriptor(getResolvableType(), getDependencyType(), getAnnotations()) :
|
||||
new TypeDescriptor(obtainMethodParameter()));
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
return typeDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether a fallback match is allowed.
|
||||
* <p>This is {@code false} by default but may be overridden to return {@code true} in order
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -1183,13 +1183,20 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
if (value != null) {
|
||||
if (value instanceof String) {
|
||||
String strVal = resolveEmbeddedValue((String) value);
|
||||
BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
|
||||
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
|
||||
getMergedBeanDefinition(beanName) : null);
|
||||
value = evaluateBeanDefinitionString(strVal, bd);
|
||||
}
|
||||
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
|
||||
return (descriptor.getField() != null ?
|
||||
converter.convertIfNecessary(value, type, descriptor.getField()) :
|
||||
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
|
||||
try {
|
||||
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
|
||||
}
|
||||
catch (UnsupportedOperationException ex) {
|
||||
// A custom TypeConverter which does not support TypeDescriptor resolution...
|
||||
return (descriptor.getField() != null ?
|
||||
converter.convertIfNecessary(value, type, descriptor.getField()) :
|
||||
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
|
||||
}
|
||||
}
|
||||
|
||||
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
@@ -1687,7 +1694,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
super.resolveCandidate(beanName, requiredType, beanFactory));
|
||||
}
|
||||
};
|
||||
return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null));
|
||||
Object result = doResolveDependency(descriptorToUse, beanName, null, null);
|
||||
return (result instanceof Optional ? (Optional<?>) result : Optional.ofNullable(result));
|
||||
}
|
||||
|
||||
|
||||
@@ -1915,8 +1923,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
private Stream<Object> resolveStream(boolean ordered) {
|
||||
DependencyDescriptor descriptorToUse = new StreamDependencyDescriptor(this.descriptor, ordered);
|
||||
Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);
|
||||
Assert.state(result instanceof Stream, "Stream expected");
|
||||
return (Stream<Object>) result;
|
||||
return (result instanceof Stream ? (Stream<Object>) result : Stream.of(result));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -41,6 +41,7 @@ import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.support.FormatterPropertyEditorAdapter;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -700,6 +701,14 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
return getTypeConverter().convertIfNecessary(value, requiredType, field);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
|
||||
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
|
||||
|
||||
return getTypeConverter().convertIfNecessary(value, requiredType, typeDescriptor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind the given property values to this binder's target.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -25,11 +25,11 @@ import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.AccessControlException;
|
||||
import java.security.Permission;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
@@ -46,7 +46,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -102,6 +102,8 @@ public class ApplicationContextExpressionTests {
|
||||
}
|
||||
});
|
||||
|
||||
ac.getBeanFactory().setConversionService(new DefaultConversionService());
|
||||
|
||||
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
|
||||
Properties placeholders = new Properties();
|
||||
placeholders.setProperty("code", "123");
|
||||
@@ -176,6 +178,9 @@ public class ApplicationContextExpressionTests {
|
||||
System.getProperties().put("country", "UK");
|
||||
assertEquals("123 UK", tb3.country);
|
||||
assertEquals("123 UK", tb3.countryFactory.getObject());
|
||||
assertEquals("123", tb3.optionalValue1.get());
|
||||
assertEquals("123", tb3.optionalValue2.get());
|
||||
assertFalse(tb3.optionalValue3.isPresent());
|
||||
assertSame(tb0, tb3.tb);
|
||||
|
||||
tb3 = (ValueTestBean) SerializationTestUtils.serializeAndDeserialize(tb3);
|
||||
@@ -209,12 +214,7 @@ public class ApplicationContextExpressionTests {
|
||||
GenericApplicationContext ac = new GenericApplicationContext();
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
|
||||
GenericConversionService cs = new GenericConversionService();
|
||||
cs.addConverter(String.class, String.class, new Converter<String, String>() {
|
||||
@Override
|
||||
public String convert(String source) {
|
||||
return source.trim();
|
||||
}
|
||||
});
|
||||
cs.addConverter(String.class, String.class, String::trim);
|
||||
ac.getBeanFactory().registerSingleton(GenericApplicationContext.CONVERSION_SERVICE_BEAN_NAME, cs);
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(PrototypeTestBean.class);
|
||||
rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
@@ -276,8 +276,7 @@ public class ApplicationContextExpressionTests {
|
||||
|
||||
@Test
|
||||
public void systemPropertiesSecurityManager() {
|
||||
GenericApplicationContext ac = new GenericApplicationContext();
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
|
||||
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
||||
|
||||
GenericBeanDefinition bd = new GenericBeanDefinition();
|
||||
bd.setBeanClass(TestBean.class);
|
||||
@@ -313,8 +312,7 @@ public class ApplicationContextExpressionTests {
|
||||
|
||||
@Test
|
||||
public void stringConcatenationWithDebugLogging() {
|
||||
GenericApplicationContext ac = new GenericApplicationContext();
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
|
||||
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
||||
|
||||
GenericBeanDefinition bd = new GenericBeanDefinition();
|
||||
bd.setBeanClass(String.class);
|
||||
@@ -365,6 +363,15 @@ public class ApplicationContextExpressionTests {
|
||||
@Value("${code} #{systemProperties.country}")
|
||||
public ObjectFactory<String> countryFactory;
|
||||
|
||||
@Value("${code}")
|
||||
private transient Optional<String> optionalValue1;
|
||||
|
||||
@Value("${code:#{null}}")
|
||||
private transient Optional<String> optionalValue2;
|
||||
|
||||
@Value("${codeX:#{null}}")
|
||||
private transient Optional<String> optionalValue3;
|
||||
|
||||
@Autowired @Qualifier("original")
|
||||
public transient TestBean tb;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -80,7 +80,7 @@ public class TypeDescriptor implements Serializable {
|
||||
*/
|
||||
public TypeDescriptor(MethodParameter methodParameter) {
|
||||
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
|
||||
this.type = this.resolvableType.resolve(methodParameter.getParameterType());
|
||||
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
|
||||
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class TypeDescriptor implements Serializable {
|
||||
* @param annotations the type annotations
|
||||
* @since 4.0
|
||||
*/
|
||||
protected TypeDescriptor(ResolvableType resolvableType, @Nullable Class<?> type, @Nullable Annotation[] annotations) {
|
||||
public TypeDescriptor(ResolvableType resolvableType, @Nullable Class<?> type, @Nullable Annotation[] annotations) {
|
||||
this.resolvableType = resolvableType;
|
||||
this.type = (type != null ? type : resolvableType.toClass());
|
||||
this.annotatedElement = new AnnotatedElementAdapter(annotations);
|
||||
@@ -616,7 +616,7 @@ public class TypeDescriptor implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a type descriptor for a nested type declared within the method parameter.
|
||||
* Create a type descriptor for a nested type declared within the method parameter.
|
||||
* <p>For example, if the methodParameter is a {@code List<String>} and the
|
||||
* nesting level is 1, the nested type descriptor will be String.class.
|
||||
* <p>If the methodParameter is a {@code List<List<String>>} and the nesting
|
||||
@@ -647,7 +647,7 @@ public class TypeDescriptor implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a type descriptor for a nested type declared within the field.
|
||||
* Create a type descriptor for a nested type declared within the field.
|
||||
* <p>For example, if the field is a {@code List<String>} and the nesting
|
||||
* level is 1, the nested type descriptor will be {@code String.class}.
|
||||
* <p>If the field is a {@code List<List<String>>} and the nesting level is
|
||||
@@ -656,8 +656,9 @@ public class TypeDescriptor implements Serializable {
|
||||
* is 1, the nested type descriptor will be String, derived from the map value.
|
||||
* <p>If the field is a {@code List<Map<Integer, String>>} and the nesting
|
||||
* level is 2, the nested type descriptor will be String, derived from the map value.
|
||||
* <p>Returns {@code null} if a nested type cannot be obtained because it was not declared.
|
||||
* For example, if the field is a {@code List<?>}, the nested type descriptor returned will be {@code null}.
|
||||
* <p>Returns {@code null} if a nested type cannot be obtained because it was not
|
||||
* declared. For example, if the field is a {@code List<?>}, the nested type
|
||||
* descriptor returned will be {@code null}.
|
||||
* @param field the field
|
||||
* @param nestingLevel the nesting level of the collection/array element or
|
||||
* map key/value declaration within the field
|
||||
@@ -672,7 +673,7 @@ public class TypeDescriptor implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a type descriptor for a nested type declared within the property.
|
||||
* Create a type descriptor for a nested type declared within the property.
|
||||
* <p>For example, if the property is a {@code List<String>} and the nesting
|
||||
* level is 1, the nested type descriptor will be {@code String.class}.
|
||||
* <p>If the property is a {@code List<List<String>>} and the nesting level
|
||||
@@ -681,9 +682,9 @@ public class TypeDescriptor implements Serializable {
|
||||
* is 1, the nested type descriptor will be String, derived from the map value.
|
||||
* <p>If the property is a {@code List<Map<Integer, String>>} and the nesting
|
||||
* level is 2, the nested type descriptor will be String, derived from the map value.
|
||||
* <p>Returns {@code null} if a nested type cannot be obtained because it was not declared.
|
||||
* For example, if the property is a {@code List<?>}, the nested type descriptor
|
||||
* returned will be {@code null}.
|
||||
* <p>Returns {@code null} if a nested type cannot be obtained because it was not
|
||||
* declared. For example, if the property is a {@code List<?>}, the nested type
|
||||
* descriptor returned will be {@code null}.
|
||||
* @param property the property
|
||||
* @param nestingLevel the nesting level of the collection/array element or
|
||||
* map key/value declaration within the property
|
||||
|
||||
Reference in New Issue
Block a user