Support String index type in custom IndexAccessor
Closes gh-32706
This commit is contained in:
parent
a3d3bc0a1f
commit
14689256c4
|
@ -244,14 +244,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to treat the index value as a property of the context object.
|
// Check for a custom IndexAccessor.
|
||||||
TypeDescriptor valueType = indexValue.getTypeDescriptor();
|
|
||||||
if (valueType != null && String.class == valueType.getType()) {
|
|
||||||
this.indexedType = IndexedType.OBJECT;
|
|
||||||
return new PropertyAccessorValueRef(
|
|
||||||
target, (String) index, state.getEvaluationContext(), targetDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
EvaluationContext evalContext = state.getEvaluationContext();
|
EvaluationContext evalContext = state.getEvaluationContext();
|
||||||
List<IndexAccessor> accessorsToTry = getIndexAccessorsToTry(target, evalContext.getIndexAccessors());
|
List<IndexAccessor> accessorsToTry = getIndexAccessorsToTry(target, evalContext.getIndexAccessors());
|
||||||
if (accessMode.supportsReads) {
|
if (accessMode.supportsReads) {
|
||||||
|
@ -285,6 +278,14 @@ public class Indexer extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// As a last resort, try to treat the index value as a property of the context object.
|
||||||
|
TypeDescriptor valueType = indexValue.getTypeDescriptor();
|
||||||
|
if (valueType != null && String.class == valueType.getType()) {
|
||||||
|
this.indexedType = IndexedType.OBJECT;
|
||||||
|
return new PropertyAccessorValueRef(
|
||||||
|
target, (String) index, state.getEvaluationContext(), targetDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
throw new SpelEvaluationException(
|
throw new SpelEvaluationException(
|
||||||
getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetDescriptor);
|
getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetDescriptor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -711,6 +711,72 @@ class IndexingTests {
|
||||||
assertThat(expr.getValue(context, arrayNode)).isSameAs(node1);
|
assertThat(expr.getValue(context, arrayNode)).isSameAs(node1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-32706
|
||||||
|
void readIndexWithStringIndexType() {
|
||||||
|
BirdNameToColorMappings birdNameMappings = new BirdNameToColorMappings();
|
||||||
|
|
||||||
|
// Without a registered BirdNameToColorMappingsIndexAccessor, we should
|
||||||
|
// be able to index into an object via a property name.
|
||||||
|
Expression propertyExpression = parser.parseExpression("['property']");
|
||||||
|
assertThat(propertyExpression.getValue(context, birdNameMappings)).isEqualTo("enigma");
|
||||||
|
|
||||||
|
context.addIndexAccessor(new BirdNameToColorMappingsIndexAccessor());
|
||||||
|
|
||||||
|
Expression expression = parser.parseExpression("['cardinal']");
|
||||||
|
assertThat(expression.getValue(context, birdNameMappings)).isEqualTo(Color.RED);
|
||||||
|
|
||||||
|
// With a registered BirdNameToColorMappingsIndexAccessor, an attempt
|
||||||
|
// to index into an object via a property name should fail.
|
||||||
|
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||||
|
.isThrownBy(() -> propertyExpression.getValue(context, birdNameMappings))
|
||||||
|
.withMessageEndingWith("A problem occurred while attempting to read index '%s' in '%s'",
|
||||||
|
"property", BirdNameToColorMappings.class.getName())
|
||||||
|
.havingCause().withMessage("unknown bird color: property");
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BirdNameToColorMappings {
|
||||||
|
|
||||||
|
public final String property = "enigma";
|
||||||
|
|
||||||
|
public Color get(String name) {
|
||||||
|
return switch (name) {
|
||||||
|
case "cardinal" -> Color.RED;
|
||||||
|
case "blue jay" -> Color.BLUE;
|
||||||
|
default -> throw new RuntimeException("unknown bird color: " + name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BirdNameToColorMappingsIndexAccessor implements IndexAccessor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?>[] getSpecificTargetClasses() {
|
||||||
|
return new Class[] { BirdNameToColorMappings.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canRead(EvaluationContext context, Object target, Object index) {
|
||||||
|
return (target instanceof BirdNameToColorMappings && index instanceof String);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypedValue read(EvaluationContext context, Object target, Object index) {
|
||||||
|
BirdNameToColorMappings mappings = (BirdNameToColorMappings) target;
|
||||||
|
String name = (String) index;
|
||||||
|
return new TypedValue(mappings.get(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canWrite(EvaluationContext context, Object target, Object index) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(EvaluationContext context, Object target, Object index, @Nullable Object newValue) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link IndexAccessor} that knows how to read and write indexes in a
|
* {@link IndexAccessor} that knows how to read and write indexes in a
|
||||||
* Jackson {@link ArrayNode}.
|
* Jackson {@link ArrayNode}.
|
||||||
|
|
Loading…
Reference in New Issue