Allow auto grow for entries w/o default constructor in SpEL
Prior to this commit, The "auto grow" feature in SpEL expressions only worked for element types with a default constructor. For example, auto grow did not work for a list of BigDecimal elements. This commit inserts a null value in the list when no default constructor can be found for the element type. Closes gh-25367
This commit is contained in:
		
							parent
							
								
									d2e1150c79
								
							
						
					
					
						commit
						35c0ae7b0c
					
				|  | @ -712,10 +712,10 @@ public class Indexer extends SpelNodeImpl { | ||||||
| 				} | 				} | ||||||
| 				TypeDescriptor elementType = this.collectionEntryDescriptor.getElementTypeDescriptor(); | 				TypeDescriptor elementType = this.collectionEntryDescriptor.getElementTypeDescriptor(); | ||||||
| 				try { | 				try { | ||||||
| 					Constructor<?> ctor = ReflectionUtils.accessibleConstructor(elementType.getType()); | 					Constructor<?> ctor = getConstructor(elementType.getType()); | ||||||
| 					int newElements = this.index - this.collection.size(); | 					int newElements = this.index - this.collection.size(); | ||||||
| 					while (newElements >= 0) { | 					while (newElements >= 0) { | ||||||
| 						this.collection.add(ctor.newInstance()); | 						this.collection.add(ctor == null ? null : ctor.newInstance()); | ||||||
| 						newElements--; | 						newElements--; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | @ -725,6 +725,15 @@ public class Indexer extends SpelNodeImpl { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		Constructor<?> getConstructor(Class<?> type) { | ||||||
|  | 			try { | ||||||
|  | 				return ReflectionUtils.accessibleConstructor(type); | ||||||
|  | 			} | ||||||
|  | 			catch (Throwable ex) { | ||||||
|  | 				return null; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		@Override | 		@Override | ||||||
| 		public boolean isWritable() { | 		public boolean isWritable() { | ||||||
| 			return true; | 			return true; | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ import java.lang.annotation.ElementType; | ||||||
| import java.lang.annotation.Retention; | import java.lang.annotation.Retention; | ||||||
| import java.lang.annotation.RetentionPolicy; | import java.lang.annotation.RetentionPolicy; | ||||||
| import java.lang.annotation.Target; | import java.lang.annotation.Target; | ||||||
|  | import java.math.BigDecimal; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
|  | @ -199,6 +200,27 @@ public class IndexingTests { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	public List<BigDecimal> decimals; | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void autoGrowWithoutDefaultConstructor() { | ||||||
|  | 		this.decimals = new ArrayList<>(); | ||||||
|  | 		SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); | ||||||
|  | 		parser.parseExpression("decimals[0]").setValue(this, "123.4"); | ||||||
|  | 		assertThat(decimals.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void indexIntoPropertyContainingNullList() { | ||||||
|  | 		this.decimals = new ArrayList<>(); | ||||||
|  | 		this.decimals.add(null); | ||||||
|  | 		this.decimals.add(BigDecimal.ONE); | ||||||
|  | 		SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); | ||||||
|  | 		parser.parseExpression("decimals[0]").setValue(this, "9876.5"); | ||||||
|  | 		assertThat(decimals.get(0)).isEqualTo(BigDecimal.valueOf(9876.5)); | ||||||
|  | 		assertThat(decimals.get(1)).isEqualTo(BigDecimal.ONE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void indexIntoPropertyContainingList() { | 	public void indexIntoPropertyContainingList() { | ||||||
| 		List<Integer> property = new ArrayList<>(); | 		List<Integer> property = new ArrayList<>(); | ||||||
|  |  | ||||||
|  | @ -339,8 +339,12 @@ index into an array or collection and the element at the specified index is `nul | ||||||
| you can automatically create the element. This is useful when using expressions made up of a | you can automatically create the element. This is useful when using expressions made up of a | ||||||
| chain of property references. If you index into an array or list | chain of property references. If you index into an array or list | ||||||
| and specifying an index that is beyond the end of the current size of the array or | and specifying an index that is beyond the end of the current size of the array or | ||||||
| list, you can automatically grow the array or list to accommodate that index. The following | list, you can automatically grow the array or list to accommodate that index. In order to add | ||||||
| example demonstrates how to automatically grow the list: | an element at the specified index, SpEL will try to create the element using a default | ||||||
|  | constructor before setting the specified value. If the element type does not have a default | ||||||
|  | constructor, `null` will be added. Note if there is no built-in or custom converter, that knows | ||||||
|  | how to set the value, `null` will remain in the array or list at the specified index. | ||||||
|  | The following example demonstrates how to automatically grow the list: | ||||||
| 
 | 
 | ||||||
| [source,java,indent=0,subs="verbatim,quotes",role="primary"] | [source,java,indent=0,subs="verbatim,quotes",role="primary"] | ||||||
| .Java | .Java | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue