From f14b122c9c87823c659d7067a32be7d109ed46a3 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 6 Dec 2023 16:51:45 +0100 Subject: [PATCH] Fix #this and #root variable examples in SpEL documentation This commit actually introduces a new example for #root variable usage, which was previously missing. Closes gh-31770 --- .../expressions/language-ref/variables.adoc | 97 +++++++++++++++---- .../spel/SpelDocumentationTests.java | 49 ++++++++-- 2 files changed, 116 insertions(+), 30 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/core/expressions/language-ref/variables.adoc b/framework-docs/modules/ROOT/pages/core/expressions/language-ref/variables.adoc index a5511ef377..11874bd75f 100644 --- a/framework-docs/modules/ROOT/pages/core/expressions/language-ref/variables.adoc +++ b/framework-docs/modules/ROOT/pages/core/expressions/language-ref/variables.adoc @@ -53,8 +53,10 @@ Kotlin:: The `#this` variable is always defined and refers to the current evaluation object (against which unqualified references are resolved). The `#root` variable is always defined and refers to the root context object. Although `#this` may vary as components of -an expression are evaluated, `#root` always refers to the root. The following examples -show how to use the `#this` and `#root` variables: +an expression are evaluated, `#root` always refers to the root. + +The following example shows how to use the `#this` variable in conjunction with +xref:core/expressions/language-ref/collection-selection.adoc[collection selection]. [tabs] ====== @@ -62,40 +64,95 @@ Java:: + [source,java,indent=0,subs="verbatim,quotes",role="primary"] ---- - // create an array of integers - List primes = new ArrayList<>(); - primes.addAll(Arrays.asList(2,3,5,7,11,13,17)); + // Create a list of prime integers. + List primes = List.of(2, 3, 5, 7, 11, 13, 17); - // create parser and set variable 'primes' as the array of integers + // Create parser and set variable 'primes' as the list of integers. ExpressionParser parser = new SpelExpressionParser(); - EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess(); + EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); context.setVariable("primes", primes); - // all prime numbers > 10 from the list (using selection ?{...}) - // evaluates to [11, 13, 17] - List primesGreaterThanTen = (List) parser.parseExpression( - "#primes.?[#this>10]").getValue(context); + // Select all prime numbers > 10 from the list (using selection ?{...}). + String expression = "#primes.?[#this > 10]"; + + // Evaluates to a list containing [11, 13, 17]. + List primesGreaterThanTen = + parser.parseExpression(expression).getValue(context, List.class); ---- Kotlin:: + [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] ---- - // create an array of integers - val primes = ArrayList() - primes.addAll(listOf(2, 3, 5, 7, 11, 13, 17)) + // Create a list of prime integers. + val primes = listOf(2, 3, 5, 7, 11, 13, 17) - // create parser and set variable 'primes' as the array of integers + // Create parser and set variable 'primes' as the list of integers. val parser = SpelExpressionParser() - val context = SimpleEvaluationContext.forReadOnlyDataAccess() + val context = SimpleEvaluationContext.forReadWriteDataBinding().build() context.setVariable("primes", primes) - // all prime numbers > 10 from the list (using selection ?{...}) - // evaluates to [11, 13, 17] - val primesGreaterThanTen = parser.parseExpression( - "#primes.?[#this>10]").getValue(context) as List + // Select all prime numbers > 10 from the list (using selection ?{...}). + val expression = "#primes.?[#this > 10]" + + // Evaluates to a list containing [11, 13, 17]. + val primesGreaterThanTen = parser.parseExpression(expression) + .getValue(context) as List ---- ====== +The following example shows how to use the `#this` and `#root` variables together in +conjunction with +xref:core/expressions/language-ref/collection-projection.adoc[collection projection]. +[tabs] +====== +Java:: ++ +[source,java,indent=0,subs="verbatim,quotes",role="primary"] +---- + // Create parser and evaluation context. + ExpressionParser parser = new SpelExpressionParser(); + EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); + + // Create an inventor to use as the root context object. + Inventor tesla = new Inventor("Nikola Tesla"); + tesla.setInventions("Telephone repeater", "Tesla coil transformer"); + + // Iterate over all inventions of the Inventor referenced as the #root + // object, and generate a list of strings whose contents take the form + // " invented the ." (using projection !{...}). + String expression = "#root.inventions.![#root.name + ' invented the ' + #this + '.']"; + + // Evaluates to a list containing: + // "Nikola Tesla invented the Telephone repeater." + // "Nikola Tesla invented the Tesla coil transformer." + List results = parser.parseExpression(expression) + .getValue(context, tesla, List.class); +---- + +Kotlin:: ++ +[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] +---- + // Create parser and evaluation context. + val parser = SpelExpressionParser() + val context = SimpleEvaluationContext.forReadWriteDataBinding().build() + + // Create an inventor to use as the root context object. + val tesla = Inventor("Nikola Tesla") + tesla.setInventions("Telephone repeater", "Tesla coil transformer") + + // Iterate over all inventions of the Inventor referenced as the #root + // object, and generate a list of strings whose contents take the form + // " invented the ." (using projection !{...}). + val expression = "#root.inventions.![#root.name + ' invented the ' + #this + '.']" + + // Evaluates to a list containing: + // "Nikola Tesla invented the Telephone repeater." + // "Nikola Tesla invented the Tesla coil transformer." + val results = parser.parseExpression(expression) + .getValue(context, tesla, List::class.java) +---- +====== diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java index 118e339d09..3d5c7d25ac 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelDocumentationTests.java @@ -20,7 +20,6 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; @@ -374,20 +373,50 @@ class SpelDocumentationTests extends AbstractExpressionTests { @Test @SuppressWarnings("unchecked") - void specialVariables() throws Exception { - // create an array of integers - List primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17); + void thisVariable() { + // Create a list of prime integers. + List primes = List.of(2, 3, 5, 7, 11, 13, 17); - // create parser and set variable 'primes' as the array of integers + // Create parser and set variable 'primes' as the list of integers. ExpressionParser parser = new SpelExpressionParser(); - StandardEvaluationContext context = new StandardEvaluationContext(); - context.setVariable("primes",primes); + EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); + context.setVariable("primes", primes); - // all prime numbers > 10 from the list (using selection ?{...}) - List primesGreaterThanTen = (List) parser.parseExpression("#primes.?[#this>10]").getValue(context); - assertThat(primesGreaterThanTen.toString()).isEqualTo("[11, 13, 17]"); + // Select all prime numbers > 10 from the list (using selection ?{...}). + String expression = "#primes.?[#this > 10]"; + + // Evaluates to a list containing [11, 13, 17]. + List primesGreaterThanTen = + parser.parseExpression(expression).getValue(context, List.class); + + assertThat(primesGreaterThanTen).containsExactly(11, 13, 17); } + @Test + @SuppressWarnings("unchecked") + void thisAndRootVariables() { + // Create parser and evaluation context. + ExpressionParser parser = new SpelExpressionParser(); + EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); + + // Create an inventor to use as the root context object. + Inventor tesla = new Inventor("Nikola Tesla"); + tesla.setInventions("Telephone repeater", "Tesla coil transformer"); + + // Iterate over all inventions of the Inventor referenced as the #root + // object, and generate a list of strings whose contents take the form + // " invented the ." (using projection !{...}). + String expression = "#root.inventions.![#root.name + ' invented the ' + #this + '.']"; + + // Evaluates to a list containing: + // Nikola Tesla invented the Telephone repeater. + // Nikola Tesla invented the Tesla coil transformer. + List results = parser.parseExpression(expression).getValue(context, tesla, List.class); + + assertThat(results).containsExactly( + "Nikola Tesla invented the Telephone repeater.", + "Nikola Tesla invented the Tesla coil transformer."); + } @Test void functions() throws Exception {