1
1
/*
2
- * Copyright 2002-2017 the original author or authors.
2
+ * Copyright 2002-2018 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .expression .spel .ast ;
18
18
19
+ import java .lang .reflect .Constructor ;
19
20
import java .lang .reflect .Field ;
20
21
import java .lang .reflect .Member ;
21
22
import java .lang .reflect .Method ;
@@ -113,22 +114,20 @@ public boolean isWritable(ExpressionState expressionState) throws SpelEvaluation
113
114
@ Override
114
115
protected ValueRef getValueRef (ExpressionState state ) throws EvaluationException {
115
116
TypedValue context = state .getActiveContextObject ();
116
- Object targetObject = context .getValue ();
117
+ Object target = context .getValue ();
117
118
TypeDescriptor targetDescriptor = context .getTypeDescriptor ();
118
- Assert .state (targetDescriptor != null , "No type descriptor" );
119
- TypedValue indexValue = null ;
120
- Object index = null ;
119
+ TypedValue indexValue ;
120
+ Object index ;
121
121
122
- // This first part of the if clause prevents a 'double dereference' of
123
- // the property (SPR-5847)
124
- if (targetObject instanceof Map && (this .children [0 ] instanceof PropertyOrFieldReference )) {
122
+ // This first part of the if clause prevents a 'double dereference' of the property (SPR-5847)
123
+ if (target instanceof Map && (this .children [0 ] instanceof PropertyOrFieldReference )) {
125
124
PropertyOrFieldReference reference = (PropertyOrFieldReference ) this .children [0 ];
126
125
index = reference .getName ();
127
126
indexValue = new TypedValue (index );
128
127
}
129
128
else {
130
- // In case the map key is unqualified, we want it evaluated against
131
- // the root object so temporarily push that on whilst evaluating the key
129
+ // In case the map key is unqualified, we want it evaluated against the root object
130
+ // so temporarily push that on whilst evaluating the key
132
131
try {
133
132
state .pushActiveContextObject (state .getRootContextObject ());
134
133
indexValue = this .children [0 ].getValueInternal (state );
@@ -140,48 +139,52 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException
140
139
}
141
140
}
142
141
142
+ // Raise a proper exception in case of a null target
143
+ if (target == null ) {
144
+ throw new SpelEvaluationException (getStartPosition (), SpelMessage .CANNOT_INDEX_INTO_NULL_VALUE );
145
+ }
146
+ // At this point, we need a TypeDescriptor for a non-null target object
147
+ Assert .state (targetDescriptor != null , "No type descriptor" );
148
+
143
149
// Indexing into a Map
144
- if (targetObject instanceof Map ) {
150
+ if (target instanceof Map ) {
145
151
Object key = index ;
146
152
if (targetDescriptor .getMapKeyTypeDescriptor () != null ) {
147
153
key = state .convertValue (key , targetDescriptor .getMapKeyTypeDescriptor ());
148
154
}
149
155
this .indexedType = IndexedType .MAP ;
150
- return new MapIndexingValueRef (state .getTypeConverter (), (Map <?, ?>) targetObject , key , targetDescriptor );
151
- }
152
-
153
- if (targetObject == null ) {
154
- throw new SpelEvaluationException (getStartPosition (), SpelMessage .CANNOT_INDEX_INTO_NULL_VALUE );
156
+ return new MapIndexingValueRef (state .getTypeConverter (), (Map <?, ?>) target , key , targetDescriptor );
155
157
}
156
158
157
- // if the object is something that looks indexable by an integer,
159
+ // If the object is something that looks indexable by an integer,
158
160
// attempt to treat the index value as a number
159
- if (targetObject .getClass ().isArray () || targetObject instanceof Collection || targetObject instanceof String ) {
161
+ if (target .getClass ().isArray () || target instanceof Collection || target instanceof String ) {
160
162
int idx = (Integer ) state .convertValue (index , TypeDescriptor .valueOf (Integer .class ));
161
- if (targetObject .getClass ().isArray ()) {
163
+ if (target .getClass ().isArray ()) {
162
164
this .indexedType = IndexedType .ARRAY ;
163
- return new ArrayIndexingValueRef (state .getTypeConverter (), targetObject , idx , targetDescriptor );
165
+ return new ArrayIndexingValueRef (state .getTypeConverter (), target , idx , targetDescriptor );
164
166
}
165
- else if (targetObject instanceof Collection ) {
166
- if (targetObject instanceof List ) {
167
+ else if (target instanceof Collection ) {
168
+ if (target instanceof List ) {
167
169
this .indexedType = IndexedType .LIST ;
168
170
}
169
- return new CollectionIndexingValueRef ((Collection <?>) targetObject , idx , targetDescriptor ,
171
+ return new CollectionIndexingValueRef ((Collection <?>) target , idx , targetDescriptor ,
170
172
state .getTypeConverter (), state .getConfiguration ().isAutoGrowCollections (),
171
173
state .getConfiguration ().getMaximumAutoGrowSize ());
172
174
}
173
175
else {
174
176
this .indexedType = IndexedType .STRING ;
175
- return new StringIndexingLValue ((String ) targetObject , idx , targetDescriptor );
177
+ return new StringIndexingLValue ((String ) target , idx , targetDescriptor );
176
178
}
177
179
}
178
180
179
181
// Try and treat the index value as a property of the context object
180
- // TODO could call the conversion service to convert the value to a String
182
+ // TODO: could call the conversion service to convert the value to a String
181
183
TypeDescriptor valueType = indexValue .getTypeDescriptor ();
182
184
if (valueType != null && String .class == valueType .getType ()) {
183
185
this .indexedType = IndexedType .OBJECT ;
184
- return new PropertyIndexingValueRef (targetObject , (String ) index , state .getEvaluationContext (), targetDescriptor );
186
+ return new PropertyIndexingValueRef (
187
+ target , (String ) index , state .getEvaluationContext (), targetDescriptor );
185
188
}
186
189
187
190
throw new SpelEvaluationException (
@@ -200,12 +203,10 @@ else if (this.indexedType == IndexedType.MAP) {
200
203
return (this .children [0 ] instanceof PropertyOrFieldReference || this .children [0 ].isCompilable ());
201
204
}
202
205
else if (this .indexedType == IndexedType .OBJECT ) {
203
- // If the string name is changing the accessor is clearly going to change (so compilation is not possible)
204
- if (this .cachedReadAccessor != null &&
205
- (this .cachedReadAccessor instanceof ReflectivePropertyAccessor .OptimalPropertyAccessor ) &&
206
- (getChild (0 ) instanceof StringLiteral )) {
207
- return true ;
208
- }
206
+ // If the string name is changing the accessor is clearly going to change (so no compilation possible)
207
+ return (this .cachedReadAccessor != null &&
208
+ this .cachedReadAccessor instanceof ReflectivePropertyAccessor .OptimalPropertyAccessor &&
209
+ getChild (0 ) instanceof StringLiteral );
209
210
}
210
211
return false ;
211
212
}
@@ -283,7 +284,8 @@ else if (this.indexedType == IndexedType.MAP) {
283
284
this .children [0 ].generateCode (mv , cf );
284
285
cf .exitCompilationScope ();
285
286
}
286
- mv .visitMethodInsn (INVOKEINTERFACE , "java/util/Map" , "get" , "(Ljava/lang/Object;)Ljava/lang/Object;" , true );
287
+ mv .visitMethodInsn (
288
+ INVOKEINTERFACE , "java/util/Map" , "get" , "(Ljava/lang/Object;)Ljava/lang/Object;" , true );
287
289
}
288
290
289
291
else if (this .indexedType == IndexedType .OBJECT ) {
@@ -592,11 +594,11 @@ public TypedValue getValue() {
592
594
}
593
595
}
594
596
catch (AccessException ex ) {
595
- throw new SpelEvaluationException (getStartPosition (), ex , SpelMessage . INDEXING_NOT_SUPPORTED_FOR_TYPE ,
596
- this .targetObjectTypeDescriptor .toString ());
597
+ throw new SpelEvaluationException (getStartPosition (), ex ,
598
+ SpelMessage . INDEXING_NOT_SUPPORTED_FOR_TYPE , this .targetObjectTypeDescriptor .toString ());
597
599
}
598
- throw new SpelEvaluationException (getStartPosition (), SpelMessage . INDEXING_NOT_SUPPORTED_FOR_TYPE ,
599
- this .targetObjectTypeDescriptor .toString ());
600
+ throw new SpelEvaluationException (getStartPosition (),
601
+ SpelMessage . INDEXING_NOT_SUPPORTED_FOR_TYPE , this .targetObjectTypeDescriptor .toString ());
600
602
}
601
603
602
604
@ Override
@@ -612,8 +614,8 @@ public void setValue(@Nullable Object newValue) {
612
614
accessor .write (this .evaluationContext , this .targetObject , this .name , newValue );
613
615
return ;
614
616
}
615
- List <PropertyAccessor > accessorsToTry =
616
- AstUtils . getPropertyAccessorsToTry ( contextObjectClass , this .evaluationContext .getPropertyAccessors ());
617
+ List <PropertyAccessor > accessorsToTry = AstUtils . getPropertyAccessorsToTry (
618
+ contextObjectClass , this .evaluationContext .getPropertyAccessors ());
617
619
for (PropertyAccessor accessor : accessorsToTry ) {
618
620
if (accessor .canWrite (this .evaluationContext , this .targetObject , this .name )) {
619
621
Indexer .this .cachedWriteName = this .name ;
@@ -625,8 +627,8 @@ public void setValue(@Nullable Object newValue) {
625
627
}
626
628
}
627
629
catch (AccessException ex ) {
628
- throw new SpelEvaluationException (getStartPosition (), ex , SpelMessage . EXCEPTION_DURING_PROPERTY_WRITE ,
629
- this .name , ex .getMessage ());
630
+ throw new SpelEvaluationException (getStartPosition (), ex ,
631
+ SpelMessage . EXCEPTION_DURING_PROPERTY_WRITE , this .name , ex .getMessage ());
630
632
}
631
633
}
632
634
@@ -652,11 +654,12 @@ private class CollectionIndexingValueRef implements ValueRef {
652
654
653
655
private final int maximumSize ;
654
656
655
- public CollectionIndexingValueRef (Collection collection , int index , TypeDescriptor collectionEntryTypeDescriptor ,
657
+ public CollectionIndexingValueRef (Collection collection , int index , TypeDescriptor collectionEntryDescriptor ,
656
658
TypeConverter typeConverter , boolean growCollection , int maximumSize ) {
659
+
657
660
this .collection = collection ;
658
661
this .index = index ;
659
- this .collectionEntryDescriptor = collectionEntryTypeDescriptor ;
662
+ this .collectionEntryDescriptor = collectionEntryDescriptor ;
660
663
this .typeConverter = typeConverter ;
661
664
this .growCollection = growCollection ;
662
665
this .maximumSize = maximumSize ;
@@ -707,13 +710,15 @@ private void growCollectionIfNecessary() {
707
710
throw new SpelEvaluationException (getStartPosition (), SpelMessage .UNABLE_TO_GROW_COLLECTION );
708
711
}
709
712
if (this .collectionEntryDescriptor .getElementTypeDescriptor () == null ) {
710
- throw new SpelEvaluationException (getStartPosition (), SpelMessage .UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE );
713
+ throw new SpelEvaluationException (
714
+ getStartPosition (), SpelMessage .UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE );
711
715
}
712
716
TypeDescriptor elementType = this .collectionEntryDescriptor .getElementTypeDescriptor ();
713
717
try {
718
+ Constructor <?> ctor = ReflectionUtils .accessibleConstructor (elementType .getType ());
714
719
int newElements = this .index - this .collection .size ();
715
720
while (newElements >= 0 ) {
716
- ( this .collection ) .add (ReflectionUtils . accessibleConstructor ( elementType . getType ()) .newInstance ());
721
+ this .collection .add (ctor .newInstance ());
717
722
newElements --;
718
723
}
719
724
}
0 commit comments