restored DataBinder's ability to bind to an auto-growing List with unknown element type (SPR-8828)
This commit is contained in:
@@ -512,11 +512,15 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||
|
||||
private Object convertForProperty(String propertyName, Object oldValue, Object newValue, PropertyDescriptor pd)
|
||||
throws TypeMismatchException {
|
||||
GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd;
|
||||
Class<?> beanClass = gpd.getBeanClass();
|
||||
|
||||
return convertIfNecessary(propertyName, oldValue, newValue, pd.getPropertyType(), new TypeDescriptor(property(pd)));
|
||||
}
|
||||
|
||||
private Property property(PropertyDescriptor pd) {
|
||||
GenericTypeAwarePropertyDescriptor typeAware = (GenericTypeAwarePropertyDescriptor) pd;
|
||||
return new Property(typeAware.getBeanClass(), typeAware.getReadMethod(), typeAware.getWriteMethod());
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation methods
|
||||
@@ -970,7 +974,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||
if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
|
||||
oldValue = Array.get(propValue, arrayIndex);
|
||||
}
|
||||
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
|
||||
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
|
||||
requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
|
||||
Array.set(propValue, arrayIndex, convertedValue);
|
||||
}
|
||||
catch (IndexOutOfBoundsException ex) {
|
||||
@@ -984,12 +989,13 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||
pd.getReadMethod(), tokens.keys.length);
|
||||
List list = (List) propValue;
|
||||
int index = Integer.parseInt(key);
|
||||
int size = list.size();
|
||||
Object oldValue = null;
|
||||
if (isExtractOldValueForEditor() && index < size) {
|
||||
if (isExtractOldValueForEditor() && index < list.size()) {
|
||||
oldValue = list.get(index);
|
||||
}
|
||||
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
|
||||
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
|
||||
requiredType, TypeDescriptor.nested(property(pd), tokens.keys.length));
|
||||
int size = list.size();
|
||||
if (index >= size && index < this.autoGrowCollectionLimit) {
|
||||
for (int i = size; i < index; i++) {
|
||||
try {
|
||||
@@ -1023,7 +1029,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||
Map map = (Map) propValue;
|
||||
// IMPORTANT: Do not pass full property name in here - property editors
|
||||
// must not kick in for map keys but rather only for map values.
|
||||
TypeDescriptor typeDescriptor = mapKeyType != null ? TypeDescriptor.valueOf(mapKeyType) : TypeDescriptor.valueOf(Object.class);
|
||||
TypeDescriptor typeDescriptor = (mapKeyType != null ?
|
||||
TypeDescriptor.valueOf(mapKeyType) : TypeDescriptor.valueOf(Object.class));
|
||||
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
|
||||
Object oldValue = null;
|
||||
if (isExtractOldValueForEditor()) {
|
||||
@@ -1031,8 +1038,8 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||
}
|
||||
// Pass full property name and old value in here, since we want full
|
||||
// conversion ability for map values.
|
||||
Object convertedMapValue = convertIfNecessary(
|
||||
propertyName, oldValue, pv.getValue(), mapValueType, TypeDescriptor.nested(property(pd), tokens.keys.length));
|
||||
Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
|
||||
mapValueType, TypeDescriptor.nested(property(pd), tokens.keys.length));
|
||||
map.put(convertedMapKey, convertedMapValue);
|
||||
}
|
||||
else {
|
||||
@@ -1194,10 +1201,5 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
|
||||
|
||||
public String[] keys;
|
||||
}
|
||||
|
||||
private Property property(PropertyDescriptor pd) {
|
||||
GenericTypeAwarePropertyDescriptor typeAware = (GenericTypeAwarePropertyDescriptor) pd;
|
||||
return new Property(typeAware.getBeanClass(), typeAware.getReadMethod(), typeAware.getWriteMethod());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
|
||||
}
|
||||
|
||||
public Class<?> getBeanClass() {
|
||||
return beanClass;
|
||||
return this.beanClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,7 +22,13 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -45,6 +51,7 @@ import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.number.NumberFormatter;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
@@ -1552,6 +1559,20 @@ public class DataBinderTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testNestedGrowingList() {
|
||||
Form form = new Form();
|
||||
DataBinder binder = new DataBinder(form, "form");
|
||||
MutablePropertyValues mpv = new MutablePropertyValues();
|
||||
mpv.add("f[list][0]", "firstValue");
|
||||
mpv.add("f[list][1]", "secondValue");
|
||||
binder.bind(mpv);
|
||||
assertFalse(binder.getBindingResult().hasErrors());
|
||||
List<Object> list = (List<Object>) form.getF().get("list");
|
||||
assertEquals("firstValue", list.get(0));
|
||||
assertEquals("secondValue", list.get(1));
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
|
||||
private static class BeanWithIntegerList {
|
||||
|
||||
@@ -1646,4 +1667,94 @@ public class DataBinderTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class GrowingList<E> extends AbstractList<E> {
|
||||
|
||||
private List<E> list;
|
||||
|
||||
public GrowingList() {
|
||||
this.list = new ArrayList<E>();
|
||||
}
|
||||
|
||||
public List<E> getWrappedList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public E get(int index) {
|
||||
if (index >= list.size()) {
|
||||
for (int i = list.size(); i < index; i++) {
|
||||
list.add(null);
|
||||
}
|
||||
list.add(null);
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return list.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
public boolean add(E o) {
|
||||
return list.add(o);
|
||||
}
|
||||
|
||||
public void add(int index, E element) {
|
||||
list.add(index, element);
|
||||
}
|
||||
|
||||
public boolean addAll(int index, Collection<? extends E> c) {
|
||||
return list.addAll(index, c);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
list.clear();
|
||||
}
|
||||
|
||||
public int indexOf(Object o) {
|
||||
return list.indexOf(o);
|
||||
}
|
||||
|
||||
public Iterator<E> iterator() {
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
public int lastIndexOf(Object o) {
|
||||
return list.lastIndexOf(o);
|
||||
}
|
||||
|
||||
public ListIterator<E> listIterator() {
|
||||
return list.listIterator();
|
||||
}
|
||||
|
||||
public ListIterator<E> listIterator(int index) {
|
||||
return list.listIterator(index);
|
||||
}
|
||||
|
||||
public E remove(int index) {
|
||||
return list.remove(index);
|
||||
}
|
||||
|
||||
public E set(int index, E element) {
|
||||
return list.set(index, element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class Form {
|
||||
|
||||
private final Map<String, Object> f;
|
||||
|
||||
public Form() {
|
||||
f = new HashMap<String, Object>();
|
||||
f.put("list", new GrowingList<Object>());
|
||||
}
|
||||
|
||||
public Map<String, Object> getF() {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Keith Donald
|
||||
* @since 3.1
|
||||
@@ -28,15 +30,15 @@ abstract class AbstractDescriptor {
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
|
||||
protected AbstractDescriptor(Class<?> type) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("type cannot be null");
|
||||
}
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public TypeDescriptor getElementTypeDescriptor() {
|
||||
@@ -73,27 +75,33 @@ abstract class AbstractDescriptor {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Annotation[] getAnnotations();
|
||||
|
||||
public AbstractDescriptor nested() {
|
||||
if (isCollection()) {
|
||||
Class<?> elementType = resolveCollectionElementType();
|
||||
return elementType != null ? nested(elementType, 0) : null;
|
||||
return (elementType != null ? nested(elementType, 0) : null);
|
||||
}
|
||||
else if (isArray()) {
|
||||
return nested(getType().getComponentType(), 0);
|
||||
}
|
||||
else if (isMap()) {
|
||||
Class<?> mapValueType = resolveMapValueType();
|
||||
return mapValueType != null ? nested(mapValueType, 1) : null;
|
||||
return (mapValueType != null ? nested(mapValueType, 1) : null);
|
||||
}
|
||||
else if (Object.class.equals(getType())) {
|
||||
// could be a collection type but we don't know about its element type,
|
||||
// so let's just assume there is an element type of type Object
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Not a collection, array, or map: cannot resolve nested value types");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// subclassing hooks
|
||||
|
||||
public abstract Annotation[] getAnnotations();
|
||||
|
||||
protected abstract Class<?> resolveCollectionElementType();
|
||||
|
||||
protected abstract Class<?> resolveMapKeyType();
|
||||
@@ -102,6 +110,7 @@ abstract class AbstractDescriptor {
|
||||
|
||||
protected abstract AbstractDescriptor nested(Class<?> type, int typeIndex);
|
||||
|
||||
|
||||
// internal helpers
|
||||
|
||||
private boolean isCollection() {
|
||||
|
||||
@@ -20,6 +20,10 @@ import java.lang.annotation.Annotation;
|
||||
import org.springframework.core.GenericCollectionTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
/**
|
||||
* @author Keith Donald
|
||||
* @since 3.1
|
||||
*/
|
||||
class BeanPropertyDescriptor extends AbstractDescriptor {
|
||||
|
||||
private final Property property;
|
||||
@@ -28,6 +32,7 @@ class BeanPropertyDescriptor extends AbstractDescriptor {
|
||||
|
||||
private final Annotation[] annotations;
|
||||
|
||||
|
||||
public BeanPropertyDescriptor(Property property) {
|
||||
super(property.getType());
|
||||
this.property = property;
|
||||
@@ -35,24 +40,25 @@ class BeanPropertyDescriptor extends AbstractDescriptor {
|
||||
this.annotations = property.getAnnotations();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
return annotations;
|
||||
return this.annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveCollectionElementType() {
|
||||
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
|
||||
return GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveMapKeyType() {
|
||||
return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter);
|
||||
return GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> resolveMapValueType() {
|
||||
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
|
||||
return GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,9 +66,10 @@ class BeanPropertyDescriptor extends AbstractDescriptor {
|
||||
MethodParameter methodParameter = new MethodParameter(this.methodParameter);
|
||||
methodParameter.increaseNestingLevel();
|
||||
methodParameter.setTypeIndexForCurrentLevel(typeIndex);
|
||||
return new BeanPropertyDescriptor(type, property, methodParameter, annotations);
|
||||
return new BeanPropertyDescriptor(type, this.property, methodParameter, this.annotations);
|
||||
}
|
||||
|
||||
|
||||
// internal
|
||||
|
||||
private BeanPropertyDescriptor(Class<?> type, Property propertyDescriptor, MethodParameter methodParameter, Annotation[] annotations) {
|
||||
@@ -72,4 +79,4 @@ class BeanPropertyDescriptor extends AbstractDescriptor {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class FieldDescriptor extends AbstractDescriptor {
|
||||
|
||||
@Override
|
||||
public Annotation[] getAnnotations() {
|
||||
return TypeDescriptor.nullSafeAnnotations(field.getAnnotations());
|
||||
return TypeDescriptor.nullSafeAnnotations(this.field.getAnnotations());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,11 +27,16 @@ import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A description of a JavaBeans Property that allows us to avoid a dependency on java.beans.PropertyDescriptor.
|
||||
* java.beans is not available in a number of environments (e.g. Android, Java ME), so this is desirable.
|
||||
* Used to build a TypeDescriptor from a property location.
|
||||
* A description of a JavaBeans Property that allows us to avoid a dependency on
|
||||
* <code>java.beans.PropertyDescriptor</code>. The <code>java.beans</code> package
|
||||
* is not available in a number of environments (e.g. Android, Java ME), so this is
|
||||
* desirable for portability of Spring's core conversion facility.
|
||||
*
|
||||
* <p>Used to build a TypeDescriptor from a property location.
|
||||
* The built TypeDescriptor can then be used to convert from/to the property type.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @since 3.1
|
||||
* @see TypeDescriptor#TypeDescriptor(Property)
|
||||
* @see TypeDescriptor#nested(Property, int)
|
||||
*/
|
||||
@@ -49,6 +54,7 @@ public final class Property {
|
||||
|
||||
private final Annotation[] annotations;
|
||||
|
||||
|
||||
public Property(Class<?> objectType, Method readMethod, Method writeMethod) {
|
||||
this.objectType = objectType;
|
||||
this.readMethod = readMethod;
|
||||
@@ -58,72 +64,77 @@ public final class Property {
|
||||
this.annotations = resolveAnnotations();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The object declaring this property, either directly or in a superclass the object extends.
|
||||
*/
|
||||
public Class<?> getObjectType() {
|
||||
return objectType;
|
||||
return this.objectType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the property e.g. 'foo'.
|
||||
* The name of the property: e.g. 'foo'
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The property type e.g. java.lang.String.
|
||||
* The property type: e.g. <code>java.lang.String</code>
|
||||
*/
|
||||
public Class<?> getType() {
|
||||
return methodParameter.getParameterType();
|
||||
return this.methodParameter.getParameterType();
|
||||
}
|
||||
|
||||
/**
|
||||
* The property getter method e.g. getFoo()
|
||||
* The property getter method: e.g. <code>getFoo()</code>
|
||||
*/
|
||||
public Method getReadMethod() {
|
||||
return readMethod;
|
||||
return this.readMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* The property setter method e.g. setFoo(String).
|
||||
* The property setter method: e.g. <code>setFoo(String)</code>
|
||||
*/
|
||||
public Method getWriteMethod() {
|
||||
return writeMethod;
|
||||
return this.writeMethod;
|
||||
}
|
||||
|
||||
|
||||
// package private
|
||||
|
||||
MethodParameter getMethodParameter() {
|
||||
return methodParameter;
|
||||
return this.methodParameter;
|
||||
}
|
||||
|
||||
Annotation[] getAnnotations() {
|
||||
return annotations;
|
||||
return this.annotations;
|
||||
}
|
||||
|
||||
|
||||
// internal helpers
|
||||
|
||||
private String resolveName() {
|
||||
if (readMethod != null) {
|
||||
int index = readMethod.getName().indexOf("get");
|
||||
if (this.readMethod != null) {
|
||||
int index = this.readMethod.getName().indexOf("get");
|
||||
if (index != -1) {
|
||||
index += 3;
|
||||
} else {
|
||||
index = readMethod.getName().indexOf("is");
|
||||
}
|
||||
else {
|
||||
index = this.readMethod.getName().indexOf("is");
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Not a getter method");
|
||||
}
|
||||
index += 2;
|
||||
}
|
||||
return StringUtils.uncapitalize(readMethod.getName().substring(index));
|
||||
} else {
|
||||
int index = writeMethod.getName().indexOf("set") + 3;
|
||||
return StringUtils.uncapitalize(this.readMethod.getName().substring(index));
|
||||
}
|
||||
else {
|
||||
int index = this.writeMethod.getName().indexOf("set") + 3;
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Not a setter method");
|
||||
}
|
||||
return StringUtils.uncapitalize(writeMethod.getName().substring(index));
|
||||
return StringUtils.uncapitalize(this.writeMethod.getName().substring(index));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +215,8 @@ public final class Property {
|
||||
private Class<?> declaringClass() {
|
||||
if (getReadMethod() != null) {
|
||||
return getReadMethod().getDeclaringClass();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return getWriteMethod().getDeclaringClass();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +164,12 @@ public class TypeDescriptor {
|
||||
|
||||
/**
|
||||
* Creates a type descriptor for a nested type declared within the field.
|
||||
* For example, if the field is a List<String> and the nestingLevel is 1, the nested type descriptor will be String.class.
|
||||
* If the field is a List<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
|
||||
* If the field is a Map<Integer, String> and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
|
||||
* If the field is a List<Map<Integer, String>> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value.
|
||||
* Returns null if a nested type cannot be obtained because it was not declared.
|
||||
* For example, if the field is a List<?>, the nested type descriptor returned will be null.
|
||||
* <p>For example, if the field is a <code>List<String></code> and the nestingLevel is 1, the nested type descriptor will be <code>String.class</code>.
|
||||
* If the field is a <code>List<List<String>></code> and the nestingLevel is 2, the nested type descriptor will also be a <code>String.class</code>.
|
||||
* If the field is a <code>Map<Integer, String></code> and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
|
||||
* If the field is a <code>List<Map<Integer, String>></code> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value.
|
||||
* Returns <code>null</code> if a nested type cannot be obtained because it was not declared.
|
||||
* For example, if the field is a <code>List<?></code>, the nested type descriptor returned will be <code>null</code>.
|
||||
* @param field the field
|
||||
* @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the field.
|
||||
* @return the nested type descriptor at the specified nestingLevel, or null if it could not be obtained
|
||||
@@ -181,15 +181,15 @@ public class TypeDescriptor {
|
||||
|
||||
/**
|
||||
* Creates a type descriptor for a nested type declared within the property.
|
||||
* For example, if the property is a List<String> and the nestingLevel is 1, the nested type descriptor will be String.class.
|
||||
* If the property is a List<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
|
||||
* If the field is a Map<Integer, String> and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
|
||||
* If the property is a List<Map<Integer, String>> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value.
|
||||
* Returns null if a nested type cannot be obtained because it was not declared.
|
||||
* For example, if the property is a List<?>, the nested type descriptor returned will be null.
|
||||
* <p>For example, if the property is a <code>List<String></code> and the nestingLevel is 1, the nested type descriptor will be <code>String.class</code>.
|
||||
* If the property is a <code>List<List<String>></code> and the nestingLevel is 2, the nested type descriptor will also be a <code>String.class</code>.
|
||||
* If the property is a <code>Map<Integer, String></code> and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
|
||||
* If the property is a <code>List<Map<Integer, String>></code> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value.
|
||||
* Returns <code>null</code> if a nested type cannot be obtained because it was not declared.
|
||||
* For example, if the property is a <code>List<?></code>, the nested type descriptor returned will be <code>null</code>.
|
||||
* @param property the property
|
||||
* @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the property.
|
||||
* @return the nested type descriptor at the specified nestingLevel, or null if it could not be obtained
|
||||
* @return the nested type descriptor at the specified nestingLevel, or <code>null</code> if it could not be obtained
|
||||
* @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types.
|
||||
*/
|
||||
public static TypeDescriptor nested(Property property, int nestingLevel) {
|
||||
|
||||
Reference in New Issue
Block a user