|
54 | 54 | *
|
55 | 55 | * <ul>
|
56 | 56 | * <li>Arrays: the n<sup>th</sup> element</li>
|
57 |
| - * <li>Collections (lists and sets): the n<sup>th</sup> element</li> |
| 57 | + * <li>Collections (lists, sets, etc.): the n<sup>th</sup> element</li> |
58 | 58 | * <li>Strings: the n<sup>th</sup> character as a {@link String}</li>
|
59 | 59 | * <li>Maps: the value for the specified key</li>
|
60 | 60 | * <li>Objects: the property with the specified name</li>
|
| 61 | + * <li>Custom Structures: via registered {@link IndexAccessor} implementations</li> |
61 | 62 | * </ul>
|
62 | 63 | *
|
63 | 64 | * <h3>Null-safe Indexing</h3>
|
|
72 | 73 | * @author Stephane Nicoll
|
73 | 74 | * @author Sam Brannen
|
74 | 75 | * @since 3.0
|
| 76 | + * @see org.springframework.expression.IndexAccessor |
| 77 | + * @see org.springframework.expression.spel.CompilableIndexAccessor |
| 78 | + * @see org.springframework.expression.spel.support.ReflectiveIndexAccessor |
75 | 79 | */
|
76 | 80 | public class Indexer extends SpelNodeImpl {
|
77 | 81 |
|
@@ -385,7 +389,7 @@ else if (this.indexedType == IndexedType.MAP) {
|
385 | 389 | mv.visitTypeInsn(CHECKCAST, "java/util/Map");
|
386 | 390 | // Special case when the key is an unquoted string literal that will be parsed as
|
387 | 391 | // a property/field reference
|
388 |
| - if ((index instanceof PropertyOrFieldReference reference)) { |
| 392 | + if (index instanceof PropertyOrFieldReference reference) { |
389 | 393 | String mapKeyName = reference.getName();
|
390 | 394 | mv.visitLdcInsn(mapKeyName);
|
391 | 395 | }
|
@@ -849,30 +853,31 @@ public TypedValue getValue() {
|
849 | 853 | exitTypeDescriptor = CodeFlow.toDescriptor(Object.class);
|
850 | 854 | return new TypedValue(o, this.collectionEntryDescriptor.elementTypeDescriptor(o));
|
851 | 855 | }
|
| 856 | + |
852 | 857 | int pos = 0;
|
853 | 858 | for (Object o : this.collection) {
|
854 | 859 | if (pos == this.index) {
|
855 | 860 | return new TypedValue(o, this.collectionEntryDescriptor.elementTypeDescriptor(o));
|
856 | 861 | }
|
857 | 862 | pos++;
|
858 | 863 | }
|
859 |
| - throw new IllegalStateException("Failed to find indexed element " + this.index + ": " + this.collection); |
| 864 | + throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, |
| 865 | + this.collection.size(), this.index); |
860 | 866 | }
|
861 | 867 |
|
862 | 868 | @Override
|
863 | 869 | public void setValue(@Nullable Object newValue) {
|
864 |
| - growCollectionIfNecessary(); |
865 |
| - if (this.collection instanceof List list) { |
866 |
| - if (this.collectionEntryDescriptor.getElementTypeDescriptor() != null) { |
867 |
| - newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue), |
868 |
| - this.collectionEntryDescriptor.getElementTypeDescriptor()); |
869 |
| - } |
870 |
| - list.set(this.index, newValue); |
871 |
| - } |
872 |
| - else { |
| 870 | + if (!(this.collection instanceof List list)) { |
873 | 871 | throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
|
874 | 872 | this.collectionEntryDescriptor.toString());
|
875 | 873 | }
|
| 874 | + |
| 875 | + growCollectionIfNecessary(); |
| 876 | + if (this.collectionEntryDescriptor.getElementTypeDescriptor() != null) { |
| 877 | + newValue = this.typeConverter.convertValue(newValue, TypeDescriptor.forObject(newValue), |
| 878 | + this.collectionEntryDescriptor.getElementTypeDescriptor()); |
| 879 | + } |
| 880 | + list.set(this.index, newValue); |
876 | 881 | }
|
877 | 882 |
|
878 | 883 | private void growCollectionIfNecessary() {
|
@@ -906,7 +911,7 @@ private void growCollectionIfNecessary() {
|
906 | 911 |
|
907 | 912 | @Override
|
908 | 913 | public boolean isWritable() {
|
909 |
| - return true; |
| 914 | + return (this.collection instanceof List); |
910 | 915 | }
|
911 | 916 |
|
912 | 917 | @Nullable
|
|
0 commit comments