Skip to content

Commit

Permalink
Polish SpEL support
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Feb 9, 2024
1 parent 99bdc42 commit e72b523
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 75 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,7 +21,7 @@
import org.springframework.expression.PropertyAccessor;

/**
* A compilable property accessor is able to generate bytecode that represents
* A compilable {@link PropertyAccessor} is able to generate bytecode that represents
* the access operation, facilitating compilation to bytecode of expressions
* that use the accessor.
*
Expand All @@ -41,12 +41,13 @@ public interface CompilablePropertyAccessor extends PropertyAccessor, Opcodes {
Class<?> getPropertyType();

/**
* Generate the bytecode the performs the access operation into the specified MethodVisitor
* using context information from the codeflow where necessary.
* Generate the bytecode the performs the access operation into the specified
* {@link MethodVisitor} using context information from the {@link CodeFlow}
* where necessary.
* @param propertyName the name of the property
* @param mv the Asm method visitor into which code should be generated
* @param cf the current state of the expression compiler
* @param methodVisitor the ASM method visitor into which code should be generated
* @param codeFlow the current state of the expression compiler
*/
void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf);
void generateCode(String propertyName, MethodVisitor methodVisitor, CodeFlow codeFlow);

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,7 +27,7 @@
import org.springframework.lang.Nullable;

/**
* A {@link org.springframework.expression.MethodResolver} variant for data binding
* An {@link org.springframework.expression.MethodResolver} variant for data binding
* purposes, using reflection to access instance methods on a given target object.
*
* <p>This accessor does not resolve static methods and also no technical methods
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,7 @@
import java.lang.reflect.Method;

/**
* A {@link org.springframework.expression.PropertyAccessor} variant for data binding
* An {@link org.springframework.expression.PropertyAccessor} variant for data binding
* purposes, using reflection to access properties for reading and possibly writing.
*
* <p>A property can be referenced through a public getter method (when being read)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -503,14 +503,10 @@ enum ArgumentsMatchKind {
* and the set that are being supplied at the point of invocation. If the kind
* indicates that conversion is required for some of the arguments then the arguments
* that require conversion are listed in the argsRequiringConversion array.
*
* @param kind the kind of match that was achieved
*/
static class ArgumentsMatchInfo {

private final ArgumentsMatchKind kind;

ArgumentsMatchInfo(ArgumentsMatchKind kind) {
this.kind = kind;
}
record ArgumentsMatchInfo(ArgumentsMatchKind kind) {

public boolean isExactMatch() {
return (this.kind == ArgumentsMatchKind.EXACT);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,7 +33,8 @@
import org.springframework.lang.Nullable;

/**
* A constructor resolver that uses reflection to locate the constructor that should be invoked.
* A constructor resolver that uses reflection to locate the constructor that
* should be invoked.
*
* @author Andy Clement
* @author Juergen Hoeller
Expand All @@ -42,12 +43,15 @@
public class ReflectiveConstructorResolver implements ConstructorResolver {

/**
* Locate a constructor on the type. There are three kinds of match that might occur:
* Locate a constructor on the type.
* <p>There are three kinds of matches that might occur:
* <ol>
* <li>An exact match where the types of the arguments match the types of the constructor
* <li>An in-exact match where the types we are looking for are subtypes of those defined on the constructor
* <li>A match where we are able to convert the arguments into those expected by the constructor, according to the
* registered type converter.
* <li>An exact match where the types of the arguments match the types of the
* constructor.</li>
* <li>An inexact match where the types we are looking for are subtypes of
* those defined on the constructor.</li>
* <li>A match where we are able to convert the arguments into those expected
* by the constructor, according to the registered type converter.</li>
* </ol>
*/
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -87,12 +87,15 @@ public final Method getMethod() {
}

/**
* Find the first public class in the methods declaring class hierarchy that declares this method.
* Sometimes the reflective method discovery logic finds a suitable method that can easily be
* called via reflection but cannot be called from generated code when compiling the expression
* because of visibility restrictions. For example if a non-public class overrides toString(),
* this helper method will walk up the type hierarchy to find the first public type that declares
* the method (if there is one!). For toString() it may walk as far as Object.
* Find the first public class in the method's declaring class hierarchy that
* declares this method.
* <p>Sometimes the reflective method discovery logic finds a suitable method
* that can easily be called via reflection but cannot be called from generated
* code when compiling the expression because of visibility restrictions. For
* example, if a non-public class overrides {@code toString()}, this helper
* method will traverse up the type hierarchy to find the first public type that
* declares the method (if there is one). For {@code toString()}, it may traverse
* as far as Object.
*/
@Nullable
public Class<?> getPublicDeclaringClass() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -63,7 +63,7 @@ public class ReflectiveMethodResolver implements MethodResolver {


public ReflectiveMethodResolver() {
this.useDistance = true;
this(true);
}

/**
Expand Down Expand Up @@ -100,12 +100,15 @@ public void registerMethodFilter(Class<?> type, @Nullable MethodFilter filter) {
}

/**
* Locate a method on a type. There are three kinds of match that might occur:
* Locate a method on the type.
* <p>There are three kinds of matches that might occur:
* <ol>
* <li>an exact match where the types of the arguments match the types of the constructor
* <li>an in-exact match where the types we are looking for are subtypes of those defined on the constructor
* <li>a match where we are able to convert the arguments into those expected by the constructor,
* according to the registered type converter
* <li>An exact match where the types of the arguments match the types of the
* method.</li>
* <li>An inexact match where the types we are looking for are subtypes of
* those defined on the method.</li>
* <li>A match where we are able to convert the arguments into those expected
* by the method, according to the registered type converter.</li>
* </ol>
*/
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -579,18 +579,7 @@ private static boolean isKotlinProperty(Method method, String methodSuffix) {
* Captures the member (method/field) to call reflectively to access a property value
* and the type descriptor for the value returned by the reflective call.
*/
private static class InvokerPair {

final Member member;

final TypeDescriptor typeDescriptor;

public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
this.member = member;
this.typeDescriptor = typeDescriptor;
}
}

private record InvokerPair(Member member, TypeDescriptor typeDescriptor) {}

private static final class PropertyCacheKey implements Comparable<PropertyCacheKey> {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -103,9 +103,9 @@ public final class SimpleEvaluationContext implements EvaluationContext {

private final TypeConverter typeConverter;

private final TypeComparator typeComparator = new StandardTypeComparator();
private final TypeComparator typeComparator = StandardTypeComparator.INSTANCE;

private final OperatorOverloader operatorOverloader = new StandardOperatorOverloader();
private final OperatorOverloader operatorOverloader = StandardOperatorOverloader.INSTANCE;

private final Map<String, Object> variables = new HashMap<>();

Expand Down Expand Up @@ -168,7 +168,7 @@ public BeanResolver getBeanResolver() {
/**
* {@code SimpleEvaluationContext} does not support use of type references.
* @return {@code TypeLocator} implementation that raises a
* {@link SpelEvaluationException} with {@link SpelMessage#TYPE_NOT_FOUND}.
* {@link SpelEvaluationException} with {@link SpelMessage#TYPE_NOT_FOUND}
*/
@Override
public TypeLocator getTypeLocator() {
Expand Down Expand Up @@ -315,7 +315,6 @@ public Builder withInstanceMethods() {
return this;
}


/**
* Register a custom {@link ConversionService}.
* <p>By default a {@link StandardTypeConverter} backed by a
Expand All @@ -327,6 +326,7 @@ public Builder withConversionService(ConversionService conversionService) {
this.typeConverter = new StandardTypeConverter(conversionService);
return this;
}

/**
* Register a custom {@link TypeConverter}.
* <p>By default a {@link StandardTypeConverter} backed by a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package org.springframework.expression.spel.support;

import java.util.List;

import org.junit.jupiter.api.Test;

import org.springframework.core.convert.TypeDescriptor;
Expand All @@ -31,14 +29,14 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

public class StandardComponentsTests {
class StandardComponentsTests {

@Test
void testStandardEvaluationContext() {
void standardEvaluationContext() {
StandardEvaluationContext context = new StandardEvaluationContext();
assertThat(context.getTypeComparator()).isNotNull();

TypeComparator tc = new StandardTypeComparator();
TypeComparator tc = StandardTypeComparator.INSTANCE;
context.setTypeComparator(tc);
assertThat(context.getTypeComparator()).isEqualTo(tc);

Expand All @@ -48,28 +46,25 @@ void testStandardEvaluationContext() {
}

@Test
void testStandardOperatorOverloader() throws EvaluationException {
OperatorOverloader oo = new StandardOperatorOverloader();
assertThat(oo.overridesOperation(Operation.ADD, null, null)).isFalse();
assertThatExceptionOfType(EvaluationException.class).isThrownBy(() ->
oo.operate(Operation.ADD, 2, 3));
void standardOperatorOverloader() {
OperatorOverloader overloader = new StandardOperatorOverloader();
assertThat(overloader.overridesOperation(Operation.ADD, null, null)).isFalse();
assertThatExceptionOfType(EvaluationException.class)
.isThrownBy(() -> overloader.operate(Operation.ADD, 2, 3));
}

@Test
void testStandardTypeLocator() {
void standardTypeLocator() {
StandardTypeLocator tl = new StandardTypeLocator();
List<String> prefixes = tl.getImportPrefixes();
assertThat(prefixes).hasSize(1);
assertThat(tl.getImportPrefixes()).hasSize(1);
tl.registerImport("java.util");
prefixes = tl.getImportPrefixes();
assertThat(prefixes).hasSize(2);
assertThat(tl.getImportPrefixes()).hasSize(2);
tl.removeImport("java.util");
prefixes = tl.getImportPrefixes();
assertThat(prefixes).hasSize(1);
assertThat(tl.getImportPrefixes()).hasSize(1);
}

@Test
void testStandardTypeConverter() throws EvaluationException {
void standardTypeConverter() {
TypeConverter tc = new StandardTypeConverter();
tc.convertValue(3, TypeDescriptor.forObject(3), TypeDescriptor.valueOf(Double.class));
}
Expand Down

0 comments on commit e72b523

Please sign in to comment.