Package org.springframework.expression.spel.support

# Class ReflectiveIndexAccessor

java.lang.Object
org.springframework.expression.spel.support.ReflectiveIndexAccessor

All Implemented Interfaces:
  `org.springframework.asm.Opcodes`, `IndexAccessor`, `CompilableIndexAccessor`, `TargetedAccessor`

public class ReflectiveIndexAccessor extends Object implements CompilableIndexAccessor
A flexible `IndexAccessor` that uses reflection to read from and optionally write to an indexed structure of a target object.

The indexed structure can be accessed through a public read-method (when being read) or a public write-method (when being written). The relationship between the read-method and write-method is based on a convention that is applicable for typical implementations of indexed structures. See the example below for details.

`ReflectiveIndexAccessor` also implements `CompilableIndexAccessor` in order to support compilation to bytecode for read access. Note, however, that the configured read-method must be invokable via a public class or public interface for compilation to succeed.

### Example

The `FruitMap` class (the `targetType`) represents a structure that is indexed via the `Color` enum (the `indexType`). The name of the read-method is `"getFruit"`, and that method returns a `String` (the `indexedValueType`). The name of the write-method is `"setFruit"`, and that method accepts a `Color` enum (the `indexType`) and a `String` (the `indexedValueType` which must match the return type of the read-method).

A read-only `IndexAccessor` for `FruitMap` can be created via `new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit")`. With that accessor registered and a `FruitMap` registered as a variable named `#fruitMap`, the SpEL expression `#fruitMap[T(example.Color).RED]` will evaluate to `"cherry"`.

A read-write `IndexAccessor` for `FruitMap` can be created via `new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit", "setFruit")`. With that accessor registered and a `FruitMap` registered as a variable named `#fruitMap`, the SpEL expression `#fruitMap[T(example.Color).RED] = 'strawberry'` can be used to change the fruit mapping for the color red from `"cherry"` to `"strawberry"`.

     package example;

     public enum Color {
         RED, ORANGE, YELLOW
     }

     public class FruitMap {

         private final Map<Color, String> map = new HashMap<>();

         public FruitMap() {
             this.map.put(Color.RED, "cherry");
             this.map.put(Color.ORANGE, "orange");
             this.map.put(Color.YELLOW, "banana");
         }

         public String getFruit(Color color) {
             return this.map.get(color);
         }

         public void setFruit(Color color, String fruit) {
             this.map.put(color, fruit);
         }
     }

Since:
  6.2
Author:
  Sam Brannen
See Also:
  - `IndexAccessor`
  - `CompilableIndexAccessor`
  - `StandardEvaluationContext`
  - `SimpleEvaluationContext`

## Field Summary

### Fields inherited from interface org.springframework.asm.Opcodes

`AALOAD, AASTORE, ACC_ABSTRACT, ACC_ANNOTATION, ACC_BRIDGE, ACC_DEPRECATED, ACC_ENUM, ACC_FINAL, ACC_INTERFACE, ACC_MANDATED, ACC_MODULE, ACC_NATIVE, ACC_OPEN, ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC, ACC_RECORD, ACC_STATIC, ACC_STATIC_PHASE, ACC_STRICT, ACC_SUPER, ACC_SYNCHRONIZED, ACC_SYNTHETIC, ACC_TRANSIENT, ACC_TRANSITIVE, ACC_VARARGS, ACC_VOLATILE, ACONST_NULL, ALOAD, ANEWARRAY, ARETURN, ARRAYLENGTH, ASM10_EXPERIMENTAL, ASM4, ASM5, ASM6, ASM7, ASM8, ASM9, ASTORE, ATHROW, BALOAD, BASTORE, BIPUSH, CALOAD, CASTORE, CHECKCAST, D2F, D2I, D2L, DADD, DALOAD, DASTORE, DCMPG, DCMPL, DCONST_0, DCONST_1, DDIV, DLOAD, DMUL, DNEG, DOUBLE, DREM, DRETURN, DSTORE, DSUB, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, F_APPEND, F_CHOP, F_FULL, F_NEW, F_SAME, F_SAME1, F2D, F2I, F2L, FADD, FALOAD, FASTORE, FCMPG, FCMPL, FCONST_0, FCONST_1, FCONST_2, FDIV, FLOAD, FLOAT, FMUL, FNEG, FREM, FRETURN, FSTORE, FSUB, GETFIELD, GETSTATIC, GOTO, H_GETFIELD, H_GETSTATIC, H_INVOKEINTERFACE, H_INVOKESPECIAL, H_INVOKESTATIC, H_INVOKEVIRTUAL, H_NEWINVOKESPECIAL, H_PUTFIELD, H_PUTSTATIC, I2B, I2C, I2D, I2F, I2L, I2S, IADD, IALOAD, IAND, IASTORE, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, ICONST_M1, IDIV, IF_ACMPEQ, IF_ACMPNE, IF_ICMPEQ, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ICMPLT, IF_ICMPNE, IFEQ, IFGE, IFGT, IFLE, IFLT, IFNE, IFNONNULL, IFNULL, IINC, ILOAD, IMUL, INEG, INSTANCEOF, INTEGER, INVOKEDYNAMIC, INVOKEINTERFACE, INVOKESPECIAL, INVOKESTATIC, INVOKEVIRTUAL, IOR, IREM, IRETURN, ISHL, ISHR, ISTORE, ISUB, IUSHR, IXOR, JSR, L2D, L2F, L2I, LADD, LALOAD, LAND, LASTORE, LCMP, LCONST_0, LCONST_1, LDC, LDIV, LLOAD, LMUL, LNEG, LONG, LOOKUPSWITCH, LOR, LREM, LRETURN, LSHL, LSHR, LSTORE, LSUB, LUSHR, LXOR, MONITORENTER, MONITOREXIT, MULTIANEWARRAY, NEW, NEWARRAY, NOP, NULL, POP, POP2, PUTFIELD, PUTSTATIC, RET, RETURN, SALOAD, SASTORE, SIPUSH, SOURCE_DEPRECATED, SOURCE_MASK, SWAP, T_BOOLEAN, T_BYTE, T_CHAR, T_DOUBLE, T_FLOAT, T_INT, T_LONG, T_SHORT, TABLESWITCH, TOP, UNINITIALIZED_THIS, V_PREVIEW, V1_1, V1_2, V1_3, V1_4, V1_5, V1_6, V1_7, V1_8, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V9`

## Constructor Summary

Constructors
Constructor
Description
`ReflectiveIndexAccessor(Class<?> targetType, Class<?> indexType, String readMethodName)`
Construct a new `ReflectiveIndexAccessor` for read-only access.
`ReflectiveIndexAccessor(Class<?> targetType, Class<?> indexType, String readMethodName, String writeMethodName)`
Construct a new `ReflectiveIndexAccessor` for read-write access.

## Method Summary

Modifier and Type
Method
Description
`boolean`
`canRead(EvaluationContext context, Object target, Object index)`
Return `true` if the supplied `target` and `index` can be assigned to the `targetType` and `indexType` configured via the constructor.
`boolean`
`canWrite(EvaluationContext context, Object target, Object index)`
Return `true` if a write-method has been configured and `canRead(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.Object)` returns `true` for the same arguments.
`void`
`generateCode(SpelNode index, org.springframework.asm.MethodVisitor mv, CodeFlow cf)`
Generate bytecode that performs the operation for reading the index.
`Class<?>`
`getIndexedValueType()`
Get the return type of the configured read-method.
`Class<?>[]`
`getSpecificTargetClasses()`
Return an array containing the `targetType` configured via the constructor.
`boolean`
`isCompilable()`
Determine if this `IndexAccessor` is currently suitable for compilation.
`TypedValue`
`read(EvaluationContext context, Object target, Object index)`
Invoke the configured read-method via reflection and return the result wrapped in a `TypedValue`.
`void`
`write(EvaluationContext context, Object target, Object index, Object newValue)`
Invoke the configured write-method via reflection.

### Methods inherited from class java.lang.Object

`clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

## Constructor Details

### ReflectiveIndexAccessor

public ReflectiveIndexAccessor(Class<?> targetType, Class<?> indexType, String readMethodName)
Construct a new `ReflectiveIndexAccessor` for read-only access.

See class-level documentation for further details and an example.

Parameters:
  `targetType` - the type of indexed structure which serves as the target of index operations
  `indexType` - the type of index used to read from the indexed structure
  `readMethodName` - the name of the method used to read from the indexed structure

### ReflectiveIndexAccessor

public ReflectiveIndexAccessor(Class<?> targetType, Class<?> indexType, String readMethodName, @Nullable String writeMethodName)
Construct a new `ReflectiveIndexAccessor` for read-write access.

See class-level documentation for further details and an example.

Parameters:
  `targetType` - the type of indexed structure which serves as the target of index operations
  `indexType` - the type of index used to read from or write to the indexed structure
  `readMethodName` - the name of the method used to read from the indexed structure
  `writeMethodName` - the name of the method used to write to the indexed structure, or `null` if writing is not supported

## Method Details

### getSpecificTargetClasses

public Class<?>[] getSpecificTargetClasses()
Return an array containing the `targetType` configured via the constructor.

Specified by:
  `getSpecificTargetClasses` in interface `TargetedAccessor`
Returns:
  an array of classes that this accessor is suitable for (or `null` or an empty array if a generic accessor)

### canRead

public boolean canRead(EvaluationContext context, Object target, Object index)
Return `true` if the supplied `target` and `index` can be assigned to the `targetType` and `indexType` configured via the constructor.

Considers primitive wrapper classes as assignable to the corresponding primitive types.

Specified by:
  `canRead` in interface `IndexAccessor`
Parameters:
  `context` - the evaluation context in which the access is being attempted
  `target` - the target object upon which the index is being accessed
  `index` - the index being accessed
Returns:
  `true` if this index accessor is able to read the index

### read

public TypedValue read(EvaluationContext context, Object target, Object index)
Invoke the configured read-method via reflection and return the result wrapped in a `TypedValue`.

Specified by:
  `read` in interface `IndexAccessor`
Parameters:
  `context` - the evaluation context in which the access is being attempted
  `target` - the target object upon which the index is being accessed
  `index` - the index being accessed
Returns:
  a TypedValue object wrapping the index value read and a type descriptor for the value

### canWrite

public boolean canWrite(EvaluationContext context, Object target, Object index)
Return `true` if a write-method has been configured and `canRead(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.Object)` returns `true` for the same arguments.

Specified by:
  `canWrite` in interface `IndexAccessor`
Parameters:
  `context` - the evaluation context in which the access is being attempted
  `target` - the target object upon which the index is being accessed
  `index` - the index being accessed
Returns:
  `true` if this index accessor is able to write to the index

### write

public void write(EvaluationContext context, Object target, Object index, @Nullable Object newValue)
Invoke the configured write-method via reflection.

Should only be invoked if `canWrite(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.Object)` returns `true` for the same arguments.

Specified by:
  `write` in interface `IndexAccessor`
Parameters:
  `context` - the evaluation context in which the access is being attempted
  `target` - the target object upon which the index is being accessed
  `index` - the index being accessed
  `newValue` - the new value for the index

### isCompilable

public boolean isCompilable()
Description copied from interface: `CompilableIndexAccessor`
Determine if this `IndexAccessor` is currently suitable for compilation.

May only be known once the index has been read.

Specified by:
  `isCompilable` in interface `CompilableIndexAccessor`
See Also:
  - `IndexAccessor.read(org.springframework.expression.EvaluationContext, Object, Object)`

### getIndexedValueType

public Class<?> getIndexedValueType()
Get the return type of the configured read-method.

Specified by:
  `getIndexedValueType` in interface `CompilableIndexAccessor`
See Also:
  - `IndexAccessor.read(org.springframework.expression.EvaluationContext, Object, Object)`

### generateCode

public void generateCode(SpelNode index, org.springframework.asm.MethodVisitor mv, CodeFlow cf)
Description copied from interface: `CompilableIndexAccessor`
Generate bytecode that performs the operation for reading the index.

Bytecode should be generated into the supplied `MethodVisitor` using context information from the `CodeFlow` where necessary.

The supplied `indexNode` should be used to generate the appropriate bytecode to load the index onto the stack. For example, given the expression `book.authors[0]`, invoking `codeFlow.generateCodeForArgument(methodVisitor, indexNode, int.class)` will ensure that the index (`0`) is available on the stack as a primitive `int`.

Will only be invoked if `CompilableIndexAccessor.isCompilable()` returns `true`.

Specified by:
  `generateCode` in interface `CompilableIndexAccessor`
Parameters:
  `index` - the `SpelNode` that represents the index being accessed
  `mv` - the ASM `MethodVisitor` into which code should be generated
  `cf` - the current state of the expression compiler