diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java index 2cf11d8d1..86e311920 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java @@ -368,26 +368,33 @@ public class ParameterBindingJsonReader extends AbstractBsonReader { if (token.getType().equals(JsonTokenType.UNQUOTED_STRING)) { - if (matcher.find()) { - - int index = computeParameterIndex(matcher.group()); - bindableValue.setValue(getBindableValueForIndex(index)); - bindableValue.setType(bsonTypeForValue(getBindableValueForIndex(index))); - return bindableValue; - } - Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(tokenValue); if (regexMatcher.find()) { String binding = regexMatcher.group(); String expression = binding.substring(3, binding.length() - 1); + Matcher inSpelMatcher = PARAMETER_BINDING_PATTERN.matcher(expression); + while (inSpelMatcher.find()) { + + int index = computeParameterIndex(inSpelMatcher.group()); + expression = expression.replace(inSpelMatcher.group(), getBindableValueForIndex(index).toString()); + } + Object value = evaluateExpression(expression); bindableValue.setValue(value); bindableValue.setType(bsonTypeForValue(value)); return bindableValue; } + if (matcher.find()) { + + int index = computeParameterIndex(matcher.group()); + bindableValue.setValue(getBindableValueForIndex(index)); + bindableValue.setType(bsonTypeForValue(getBindableValueForIndex(index))); + return bindableValue; + } + bindableValue.setValue(tokenValue); bindableValue.setType(BsonType.STRING); return bindableValue; @@ -396,28 +403,37 @@ public class ParameterBindingJsonReader extends AbstractBsonReader { String computedValue = tokenValue; - boolean matched = false; + + + Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(computedValue); + + while (regexMatcher.find()) { + + String binding = regexMatcher.group(); + String expression = binding.substring(3, binding.length() - 1); + + Matcher inSpelMatcher = PARAMETER_BINDING_PATTERN.matcher(expression); + while (inSpelMatcher.find()) { + + int index = computeParameterIndex(inSpelMatcher.group()); + expression = expression.replace(inSpelMatcher.group(), getBindableValueForIndex(index).toString()); + } + + computedValue = computedValue.replace(binding, nullSafeToString(evaluateExpression(expression))); + + bindableValue.setValue(computedValue); + bindableValue.setType(BsonType.STRING); + + return bindableValue; + } + while (matcher.find()) { - matched = true; String group = matcher.group(); int index = computeParameterIndex(group); computedValue = computedValue.replace(group, nullSafeToString(getBindableValueForIndex(index))); } - if (!matched) { - - Matcher regexMatcher = EXPRESSION_BINDING_PATTERN.matcher(tokenValue); - - while (regexMatcher.find()) { - - String binding = regexMatcher.group(); - String expression = binding.substring(3, binding.length() - 1); - - computedValue = computedValue.replace(binding, nullSafeToString(evaluateExpression(expression))); - } - } - bindableValue.setValue(computedValue); bindableValue.setType(BsonType.STRING); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java index c3257b5f7..6e6e14dbd 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReaderUnitTests.java @@ -26,6 +26,7 @@ import java.util.List; import org.bson.Document; import org.bson.codecs.DecoderContext; import org.junit.jupiter.api.Test; +import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.expression.EvaluationContext; import org.springframework.expression.TypedValue; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -264,10 +265,60 @@ class ParameterBindingJsonReaderUnitTests { assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"v1\": {\"$in\": [1]}}]}")); } + @Test // DATAMONGO-2545 + void shouldABindArgumentsViaIndexInSpelExpressions() { + + Object[] args = new Object[] { "yess", "nooo" }; + StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT + .getEvaluationContext(args); + + ParameterBindingJsonReader reader = new ParameterBindingJsonReader( + "{ 'isBatman' : ?#{ T(" + this.getClass().getName() + ").isBatman() ? [0] : [1] }}", + new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); + Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); + + assertThat(target).isEqualTo(new Document("isBatman", "nooo")); + } + + @Test // DATAMONGO-2545 + void shouldAllowMethodArgumentPlaceholdersInSpelExpressions/*becuase this worked before*/() { + + Object[] args = new Object[] { "yess", "nooo" }; + StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT + .getEvaluationContext(args); + + ParameterBindingJsonReader reader = new ParameterBindingJsonReader( + "{ 'isBatman' : ?#{ T(" + this.getClass().getName() + ").isBatman() ? '?0' : '?1' }}", + new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); + Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); + + assertThat(target).isEqualTo(new Document("isBatman", "nooo")); + } + + @Test // DATAMONGO-2545 + void shouldAllowMethodArgumentPlaceholdersInQuotedSpelExpressions/*becuase this worked before*/() { + + Object[] args = new Object[] { "yess", "nooo" }; + StandardEvaluationContext evaluationContext = (StandardEvaluationContext) EvaluationContextProvider.DEFAULT + .getEvaluationContext(args); + + ParameterBindingJsonReader reader = new ParameterBindingJsonReader( + "{ 'isBatman' : \"?#{ T(" + this.getClass().getName() + ").isBatman() ? '?0' : '?1' }\" }", + new ParameterBindingContext((index) -> args[index], new SpelExpressionParser(), evaluationContext)); + Document target = new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); + + assertThat(target).isEqualTo(new Document("isBatman", "nooo")); + } + private static Document parse(String json, Object... args) { ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args); return new ParameterBindingDocumentCodec().decode(reader, DecoderContext.builder().build()); } + // DATAMONGO-2545 + public static boolean isBatman() { + return false; + } + }