protected internal override IValueRef GetValueRef(ExpressionState state) { var context = state.GetActiveContextObject(); var target = context.Value; var targetDescriptor = context.TypeDescriptor; ITypedValue indexValue; object index; // This first part of the if clause prevents a 'double dereference' of the property (SPR-5847) if (target is System.Collections.IDictionary && (_children[0] is PropertyOrFieldReference reference1)) { var reference = reference1; index = reference.Name; indexValue = new TypedValue(index); } else { // In case the map key is unqualified, we want it evaluated against the root object // so temporarily push that on whilst evaluating the key try { state.PushActiveContextObject(state.RootContextObject); indexValue = _children[0].GetValueInternal(state); index = indexValue.Value; if (index == null) { throw new InvalidOperationException("No index"); } } finally { state.PopActiveContextObject(); } } // Raise a proper exception in case of a null target if (target == null) { throw new SpelEvaluationException(StartPosition, SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE); } // At this point, we need a TypeDescriptor for a non-null target object if (targetDescriptor == null) { throw new InvalidOperationException("No type descriptor"); } // Indexing into a Map if (target is System.Collections.IDictionary) { var key = index; var mapkeyType = ReflectionHelper.GetMapKeyTypeDescriptor(targetDescriptor); if (mapkeyType != null) { key = state.ConvertValue(key, mapkeyType); } _indexedType = IndexedType.MAP; return(new MapIndexingValueRef(this, state.TypeConverter, (IDictionary)target, key, targetDescriptor)); } // If the object is something that looks indexable by an integer, // attempt to treat the index value as a number if (target is Array || target is IList || target is string) { var idx = (int)state.ConvertValue(index, typeof(int)); if (target is Array) { _indexedType = IndexedType.ARRAY; return(new ArrayIndexingValueRef(this, state.TypeConverter, target, idx, targetDescriptor)); } else if (target is IList list) { _indexedType = IndexedType.LIST; return(new CollectionIndexingValueRef(this, list, idx, targetDescriptor, state.TypeConverter, state.Configuration.AutoGrowCollections, state.Configuration.MaximumAutoGrowSize)); } else { _indexedType = IndexedType.STRING; return(new StringIndexingLValue(this, (string)target, idx, targetDescriptor)); } } // Try and treat the index value as a property of the context object // Could call the conversion service to convert the value to a String var valueType = indexValue.TypeDescriptor; if (valueType != null && typeof(string) == valueType) { _indexedType = IndexedType.OBJECT; return(new PropertyIndexingValueRef(this, target, (string)index, state.EvaluationContext, targetDescriptor)); } throw new SpelEvaluationException(StartPosition, SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetDescriptor); }