Skip to content

Commit 8221c9a

Browse files
committed
Support for nested path using field access
This commit adds a nested path support for DirectFieldAccessor that is similar to what BeanWrapper provides. It is now possible to use expressions such as "person.address.city.name" to access the name of the city that a given person lives in using fields to traverse the graph. DirectFieldAccessor also now supports an auto-grow option to create a default instance for a "null" intermediate path. This option is false by default and leads to a NullValueInNestedPathException in such a case. This commit also harmonizes part of the tests suite so that core tests are shared between BeanWrapperImpl and DirectFieldAccessor. Note that map and list access is not implemented as part of this commit. Issue: SPR-9705
1 parent 442bd68 commit 8221c9a

File tree

9 files changed

+669
-154
lines changed

9 files changed

+669
-154
lines changed

spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
* implementation of actual property access left to subclasses.
2828
*
2929
* @author Juergen Hoeller
30+
* @author Stephane Nicoll
3031
* @since 2.0
3132
* @see #getPropertyValue
3233
* @see #setPropertyValue
@@ -35,6 +36,8 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl
3536

3637
private boolean extractOldValueForEditor = false;
3738

39+
private boolean autoGrowNestedPaths = false;
40+
3841

3942
@Override
4043
public void setExtractOldValueForEditor(boolean extractOldValueForEditor) {
@@ -46,6 +49,16 @@ public boolean isExtractOldValueForEditor() {
4649
return this.extractOldValueForEditor;
4750
}
4851

52+
@Override
53+
public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
54+
this.autoGrowNestedPaths = autoGrowNestedPaths;
55+
}
56+
57+
@Override
58+
public boolean isAutoGrowNestedPaths() {
59+
return this.autoGrowNestedPaths;
60+
}
61+
4962

5063
@Override
5164
public void setPropertyValue(PropertyValue pv) throws BeansException {

spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,22 +78,6 @@ public interface BeanWrapper extends ConfigurablePropertyAccessor {
7878
*/
7979
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
8080

81-
/**
82-
* Set whether this BeanWrapper should attempt to "auto-grow" a
83-
* nested path that contains a {@code null} value.
84-
* <p>If {@code true}, a {@code null} path location will be populated
85-
* with a default object value and traversed instead of resulting in a
86-
* {@link NullValueInNestedPathException}. Turning this flag on also enables
87-
* auto-growth of collection elements when accessing an out-of-bounds index.
88-
* <p>Default is {@code false} on a plain BeanWrapper.
89-
*/
90-
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
91-
92-
/**
93-
* Return whether "auto-growing" of nested paths has been activated.
94-
*/
95-
boolean isAutoGrowNestedPaths();
96-
9781
/**
9882
* Specify a limit for array and collection auto-growing.
9983
* <p>Default is unlimited on a plain BeanWrapper.

spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
115115
*/
116116
private Map<String, BeanWrapperImpl> nestedBeanWrappers;
117117

118-
private boolean autoGrowNestedPaths = false;
119-
120118
private int autoGrowCollectionLimit = Integer.MAX_VALUE;
121119

122120

@@ -252,25 +250,7 @@ public final Class<?> getRootClass() {
252250
return (this.rootObject != null ? this.rootObject.getClass() : null);
253251
}
254252

255-
/**
256-
* Set whether this BeanWrapper should attempt to "auto-grow" a nested path that contains a null value.
257-
* <p>If "true", a null path location will be populated with a default object value and traversed
258-
* instead of resulting in a {@link NullValueInNestedPathException}. Turning this flag on also
259-
* enables auto-growth of collection elements when accessing an out-of-bounds index.
260-
* <p>Default is "false" on a plain BeanWrapper.
261-
*/
262-
@Override
263-
public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
264-
this.autoGrowNestedPaths = autoGrowNestedPaths;
265-
}
266253

267-
/**
268-
* Return whether "auto-growing" of nested paths has been activated.
269-
*/
270-
@Override
271-
public boolean isAutoGrowNestedPaths() {
272-
return this.autoGrowNestedPaths;
273-
}
274254

275255
/**
276256
* Specify a limit for array and collection auto-growing.
@@ -570,7 +550,7 @@ private BeanWrapperImpl getNestedBeanWrapper(String nestedProperty) {
570550
String canonicalName = tokens.canonicalName;
571551
Object propertyValue = getPropertyValue(tokens);
572552
if (propertyValue == null) {
573-
if (this.autoGrowNestedPaths) {
553+
if (isAutoGrowNestedPaths()) {
574554
propertyValue = setDefaultValue(tokens);
575555
}
576556
else {
@@ -761,7 +741,7 @@ public Object run() throws Exception {
761741

762742
if (tokens.keys != null) {
763743
if (value == null) {
764-
if (this.autoGrowNestedPaths) {
744+
if (isAutoGrowNestedPaths()) {
765745
value = setDefaultValue(tokens.actualName);
766746
}
767747
else {
@@ -851,7 +831,7 @@ else if (value instanceof Map) {
851831
}
852832

853833
private Object growArrayIfNecessary(Object array, int index, String name) {
854-
if (!this.autoGrowNestedPaths) {
834+
if (!isAutoGrowNestedPaths()) {
855835
return array;
856836
}
857837
int length = Array.getLength(array);
@@ -874,7 +854,7 @@ private Object growArrayIfNecessary(Object array, int index, String name) {
874854
private void growCollectionIfNecessary(Collection<Object> collection, int index, String name,
875855
PropertyDescriptor pd, int nestingLevel) {
876856

877-
if (!this.autoGrowNestedPaths) {
857+
if (!isAutoGrowNestedPaths()) {
878858
return;
879859
}
880860
int size = collection.size();
@@ -951,7 +931,7 @@ private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) thro
951931
String key = tokens.keys[tokens.keys.length - 1];
952932
if (propValue == null) {
953933
// null map value case
954-
if (this.autoGrowNestedPaths) {
934+
if (isAutoGrowNestedPaths()) {
955935
// TODO: cleanup, this is pretty hacky
956936
int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
957937
getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);

spring-beans/src/main/java/org/springframework/beans/ConfigurablePropertyAccessor.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2009 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
* <p>Serves as base interface for {@link BeanWrapper}.
2727
*
2828
* @author Juergen Hoeller
29+
* @author Stephane Nicoll
2930
* @since 2.0
3031
* @see BeanWrapper
3132
*/
@@ -54,4 +55,19 @@ public interface ConfigurablePropertyAccessor extends PropertyAccessor, Property
5455
*/
5556
boolean isExtractOldValueForEditor();
5657

58+
/**
59+
* Set whether this instance should attempt to "auto-grow" a
60+
* nested path that contains a {@code null} value.
61+
* <p>If {@code true}, a {@code null} path location will be populated
62+
* with a default object value and traversed instead of resulting in a
63+
* {@link NullValueInNestedPathException}.
64+
* <p>Default is {@code false} on a plain instance.
65+
*/
66+
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
67+
68+
/**
69+
* Return whether "auto-growing" of nested paths has been activated.
70+
*/
71+
boolean isAutoGrowNestedPaths();
72+
5773
}

0 commit comments

Comments
 (0)