Browse Source

Polishing (backported from main)

pull/32357/head
Juergen Hoeller 2 years ago
parent
commit
5fd9fab0ce
  1. 12
      framework-docs/modules/ROOT/pages/core/expressions/language-ref/collection-projection.adoc
  2. 23
      framework-docs/modules/ROOT/pages/core/expressions/language-ref/collection-selection.adoc
  3. 2
      framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/server-setup-options.adoc
  4. 5
      spring-expression/src/main/java/org/springframework/expression/TypedValue.java
  5. 21
      spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java
  6. 12
      spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java

12
framework-docs/modules/ROOT/pages/core/expressions/language-ref/collection-projection.adoc

@ -4,7 +4,7 @@
Projection lets a collection drive the evaluation of a sub-expression, and the result is Projection lets a collection drive the evaluation of a sub-expression, and the result is
a new collection. The syntax for projection is `.![projectionExpression]`. For example, a new collection. The syntax for projection is `.![projectionExpression]`. For example,
suppose we have a list of inventors but want the list of cities where they were born. suppose we have a list of inventors but want the list of cities where they were born.
Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor Effectively, we want to evaluate `placeOfBirth.city` for every entry in the inventor
list. The following example uses projection to do so: list. The following example uses projection to do so:
[tabs] [tabs]
@ -13,16 +13,18 @@ Java::
+ +
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
---- ----
// returns ['Smiljan', 'Idvor' ] // evaluates to ["SmilJan", "Idvor"]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]"); List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext, List.class);
---- ----
Kotlin:: Kotlin::
+ +
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
---- ----
// returns ['Smiljan', 'Idvor' ] // evaluates to ["SmilJan", "Idvor"]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*> val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext) as List<*>
---- ----
====== ======

23
framework-docs/modules/ROOT/pages/core/expressions/language-ref/collection-selection.adoc

@ -28,13 +28,14 @@ Kotlin::
====== ======
Selection is supported for arrays and anything that implements `java.lang.Iterable` or Selection is supported for arrays and anything that implements `java.lang.Iterable` or
`java.util.Map`. For a list or array, the selection criteria is evaluated against each `java.util.Map`. For an array or `Iterable`, the selection expression is evaluated
individual element. Against a map, the selection criteria is evaluated against each map against each individual element. Against a map, the selection expression is evaluated
entry (objects of the Java type `Map.Entry`). Each map entry has its `key` and `value` against each map entry (objects of the Java type `Map.Entry`). Each map entry has its
accessible as properties for use in the selection. `key` and `value` accessible as properties for use in the selection.
The following expression returns a new map that consists of those elements of the Given a `Map` stored in a variable named `#map`, the following expression returns a new
original map where the entry's value is less than 27: map that consists of those elements of the original map where the entry's value is less
than 27:
[tabs] [tabs]
====== ======
@ -42,21 +43,21 @@ Java::
+ +
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
---- ----
Map newMap = parser.parseExpression("map.?[value<27]").getValue(); Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);
---- ----
Kotlin:: Kotlin::
+ +
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
---- ----
val newMap = parser.parseExpression("map.?[value<27]").getValue() val newMap = parser.parseExpression("#map.?[value < 27]").getValue() as Map
---- ----
====== ======
In addition to returning all the selected elements, you can retrieve only the first or In addition to returning all the selected elements, you can retrieve only the first or
the last element. To obtain the first element matching the selection, the syntax is the last element. To obtain the first element matching the selection expression, the
`.^[selectionExpression]`. To obtain the last matching selection, the syntax is syntax is `.^[selectionExpression]`. To obtain the last element matching the selection
`.$[selectionExpression]`. expression, the syntax is `.$[selectionExpression]`.

2
framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/server-setup-options.adoc

@ -152,7 +152,7 @@ Kotlin::
@Autowired @Autowired
lateinit var accountService: AccountService lateinit var accountService: AccountService
lateinit mockMvc: MockMvc lateinit var mockMvc: MockMvc
@BeforeEach @BeforeEach
fun setup(wac: WebApplicationContext) { fun setup(wac: WebApplicationContext) {

5
spring-expression/src/main/java/org/springframework/expression/TypedValue.java

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,8 @@ import org.springframework.util.ObjectUtils;
/** /**
* Encapsulates an object and a {@link TypeDescriptor} that describes it. * Encapsulates an object and a {@link TypeDescriptor} that describes it.
* The type descriptor can contain generic declarations that would not *
* <p>The type descriptor can contain generic declarations that would not
* be accessible through a simple {@code getClass()} call on the object. * be accessible through a simple {@code getClass()} call on the object.
* *
* @author Andy Clement * @author Andy Clement

21
spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -36,7 +36,9 @@ import org.springframework.util.ObjectUtils;
/** /**
* Represents selection over a map or collection. * Represents selection over a map or collection.
* For example: {1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'} returns [2, 4, 6, 8, 10] *
* <p>For example, <code>{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this)}</code> evaluates
* to {@code [2, 4, 6, 8, 10]}.
* *
* <p>Basically a subset of the input data is returned based on the * <p>Basically a subset of the input data is returned based on the
* evaluation of the expression supplied as selection criteria. * evaluation of the expression supplied as selection criteria.
@ -100,11 +102,10 @@ public class Selection extends SpelNodeImpl {
Object val = selectionCriteria.getValueInternal(state).getValue(); Object val = selectionCriteria.getValueInternal(state).getValue();
if (val instanceof Boolean b) { if (val instanceof Boolean b) {
if (b) { if (b) {
result.put(entry.getKey(), entry.getValue());
if (this.variant == FIRST) { if (this.variant == FIRST) {
result.put(entry.getKey(), entry.getValue());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this); return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this);
} }
result.put(entry.getKey(), entry.getValue());
lastKey = entry.getKey(); lastKey = entry.getKey();
} }
} }
@ -120,22 +121,22 @@ public class Selection extends SpelNodeImpl {
} }
if ((this.variant == FIRST || this.variant == LAST) && result.isEmpty()) { if ((this.variant == FIRST || this.variant == LAST) && result.isEmpty()) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(null), this); return new ValueRef.TypedValueHolderValueRef(TypedValue.NULL, this);
} }
if (this.variant == LAST) { if (this.variant == LAST) {
Map<Object, Object> resultMap = new HashMap<>(); Map<Object, Object> resultMap = new HashMap<>();
Object lastValue = result.get(lastKey); Object lastValue = result.get(lastKey);
resultMap.put(lastKey,lastValue); resultMap.put(lastKey, lastValue);
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultMap),this); return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultMap), this);
} }
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this); return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this);
} }
if (operand instanceof Iterable || ObjectUtils.isArray(operand)) { if (operand instanceof Iterable || ObjectUtils.isArray(operand)) {
Iterable<?> data = (operand instanceof Iterable<?> iterable ? Iterable<?> data = (operand instanceof Iterable<?> iterable ? iterable :
iterable : Arrays.asList(ObjectUtils.toObjectArray(operand))); Arrays.asList(ObjectUtils.toObjectArray(operand)));
List<Object> result = new ArrayList<>(); List<Object> result = new ArrayList<>();
int index = 0; int index = 0;

12
spring-webmvc/src/main/java/org/springframework/web/servlet/function/RequestPredicates.java

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -106,6 +106,7 @@ public abstract class RequestPredicates {
* against the given path pattern. * against the given path pattern.
* @param pattern the pattern to match to * @param pattern the pattern to match to
* @return a predicate that tests against the given path pattern * @return a predicate that tests against the given path pattern
* @see org.springframework.web.util.pattern.PathPattern
*/ */
public static RequestPredicate path(String pattern) { public static RequestPredicate path(String pattern) {
Assert.notNull(pattern, "'pattern' must not be null"); Assert.notNull(pattern, "'pattern' must not be null");
@ -168,6 +169,7 @@ public abstract class RequestPredicates {
* @param pattern the path pattern to match against * @param pattern the path pattern to match against
* @return a predicate that matches if the request method is GET and if the given pattern * @return a predicate that matches if the request method is GET and if the given pattern
* matches against the request path * matches against the request path
* @see org.springframework.web.util.pattern.PathPattern
*/ */
public static RequestPredicate GET(String pattern) { public static RequestPredicate GET(String pattern) {
return method(HttpMethod.GET).and(path(pattern)); return method(HttpMethod.GET).and(path(pattern));
@ -179,6 +181,7 @@ public abstract class RequestPredicates {
* @param pattern the path pattern to match against * @param pattern the path pattern to match against
* @return a predicate that matches if the request method is HEAD and if the given pattern * @return a predicate that matches if the request method is HEAD and if the given pattern
* matches against the request path * matches against the request path
* @see org.springframework.web.util.pattern.PathPattern
*/ */
public static RequestPredicate HEAD(String pattern) { public static RequestPredicate HEAD(String pattern) {
return method(HttpMethod.HEAD).and(path(pattern)); return method(HttpMethod.HEAD).and(path(pattern));
@ -190,6 +193,7 @@ public abstract class RequestPredicates {
* @param pattern the path pattern to match against * @param pattern the path pattern to match against
* @return a predicate that matches if the request method is POST and if the given pattern * @return a predicate that matches if the request method is POST and if the given pattern
* matches against the request path * matches against the request path
* @see org.springframework.web.util.pattern.PathPattern
*/ */
public static RequestPredicate POST(String pattern) { public static RequestPredicate POST(String pattern) {
return method(HttpMethod.POST).and(path(pattern)); return method(HttpMethod.POST).and(path(pattern));
@ -201,6 +205,7 @@ public abstract class RequestPredicates {
* @param pattern the path pattern to match against * @param pattern the path pattern to match against
* @return a predicate that matches if the request method is PUT and if the given pattern * @return a predicate that matches if the request method is PUT and if the given pattern
* matches against the request path * matches against the request path
* @see org.springframework.web.util.pattern.PathPattern
*/ */
public static RequestPredicate PUT(String pattern) { public static RequestPredicate PUT(String pattern) {
return method(HttpMethod.PUT).and(path(pattern)); return method(HttpMethod.PUT).and(path(pattern));
@ -212,6 +217,7 @@ public abstract class RequestPredicates {
* @param pattern the path pattern to match against * @param pattern the path pattern to match against
* @return a predicate that matches if the request method is PATCH and if the given pattern * @return a predicate that matches if the request method is PATCH and if the given pattern
* matches against the request path * matches against the request path
* @see org.springframework.web.util.pattern.PathPattern
*/ */
public static RequestPredicate PATCH(String pattern) { public static RequestPredicate PATCH(String pattern) {
return method(HttpMethod.PATCH).and(path(pattern)); return method(HttpMethod.PATCH).and(path(pattern));
@ -223,6 +229,7 @@ public abstract class RequestPredicates {
* @param pattern the path pattern to match against * @param pattern the path pattern to match against
* @return a predicate that matches if the request method is DELETE and if the given pattern * @return a predicate that matches if the request method is DELETE and if the given pattern
* matches against the request path * matches against the request path
* @see org.springframework.web.util.pattern.PathPattern
*/ */
public static RequestPredicate DELETE(String pattern) { public static RequestPredicate DELETE(String pattern) {
return method(HttpMethod.DELETE).and(path(pattern)); return method(HttpMethod.DELETE).and(path(pattern));
@ -234,6 +241,7 @@ public abstract class RequestPredicates {
* @param pattern the path pattern to match against * @param pattern the path pattern to match against
* @return a predicate that matches if the request method is OPTIONS and if the given pattern * @return a predicate that matches if the request method is OPTIONS and if the given pattern
* matches against the request path * matches against the request path
* @see org.springframework.web.util.pattern.PathPattern
*/ */
public static RequestPredicate OPTIONS(String pattern) { public static RequestPredicate OPTIONS(String pattern) {
return method(HttpMethod.OPTIONS).and(path(pattern)); return method(HttpMethod.OPTIONS).and(path(pattern));
@ -317,7 +325,6 @@ public abstract class RequestPredicates {
else { else {
return newPattern; return newPattern;
} }
} }
@ -337,6 +344,7 @@ public abstract class RequestPredicates {
* Receive notification of a path predicate. * Receive notification of a path predicate.
* @param pattern the path pattern that makes up the predicate * @param pattern the path pattern that makes up the predicate
* @see RequestPredicates#path(String) * @see RequestPredicates#path(String)
* @see org.springframework.web.util.pattern.PathPattern
*/ */
void path(String pattern); void path(String pattern);

Loading…
Cancel
Save