diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java index df1fde90a..10c34bb39 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ExpressionEvaluatingParameterBinder.java @@ -38,6 +38,7 @@ import com.mongodb.util.JSON; * * @author Christoph Strobl * @author Thomas Darimont + * @author Oliver Gierke * @since 1.9 */ class ExpressionEvaluatingParameterBinder { @@ -62,13 +63,13 @@ class ExpressionEvaluatingParameterBinder { } /** - * Bind values provided by {@link MongoParameterAccessor} to placeholders in {@literal raw} while consisdering + * Bind values provided by {@link MongoParameterAccessor} to placeholders in {@literal raw} while considering * potential conversions and parameter types. * - * @param raw - * @param accessor - * @param bindingContext - * @return {@literal null} if given {@literal raw} value is empty. + * @param raw can be {@literal null} or empty. + * @param accessor must not be {@literal null}. + * @param bindingContext must not be {@literal null}. + * @return {@literal null} if given {@code raw} value is empty. */ public String bind(String raw, MongoParameterAccessor accessor, BindingContext bindingContext) { @@ -80,12 +81,11 @@ class ExpressionEvaluatingParameterBinder { } /** - * Replaced the parameter place-holders with the actual parameter values from the given {@link ParameterBinding}s. + * Replaced the parameter placeholders with the actual parameter values from the given {@link ParameterBinding}s. * - * @param input - * @param accessor - * @param parameters - * @param bindings + * @param input must not be {@literal null} or empty. + * @param accessor must not be {@literal null}. + * @param bindings must not be {@literal null}. * @return */ private String replacePlaceholders(String input, MongoParameterAccessor accessor, BindingContext bindingContext) { @@ -95,7 +95,6 @@ class ExpressionEvaluatingParameterBinder { } boolean isCompletlyParameterizedQuery = input.matches("^\\?\\d+$"); - StringBuilder result = new StringBuilder(input); for (ParameterBinding binding : bindingContext.getBindings()) { @@ -103,32 +102,31 @@ class ExpressionEvaluatingParameterBinder { String parameter = binding.getParameter(); int idx = result.indexOf(parameter); - if (idx != -1) { - String valueForBinding = getParameterValueForBinding(accessor, bindingContext.getParameters(), binding); - - // if the value to bind is an object literal we need to remove the quoting around - // the expression insertion point. - boolean shouldPotentiallyRemoveQuotes = valueForBinding.startsWith("{") && !isCompletlyParameterizedQuery; - - int start = idx; - int end = idx + parameter.length(); - - if (shouldPotentiallyRemoveQuotes) { - - // is the insertion point actually surrounded by quotes? - char beforeStart = result.charAt(start - 1); - char afterEnd = result.charAt(end); - - if ((beforeStart == '\'' || beforeStart == '"') && (afterEnd == '\'' || afterEnd == '"')) { - - // skip preceeding and following quote - start -= 1; - end += 1; - } - } - - result.replace(start, end, valueForBinding); + if (idx == -1) { + continue; } + + String valueForBinding = getParameterValueForBinding(accessor, bindingContext.getParameters(), binding); + + int start = idx; + int end = idx + parameter.length(); + + // If the value to bind is an object literal we need to remove the quoting around the expression insertion point. + if (valueForBinding.startsWith("{") && !isCompletlyParameterizedQuery) { + + // Is the insertion point actually surrounded by quotes? + char beforeStart = result.charAt(start - 1); + char afterEnd = result.charAt(end); + + if ((beforeStart == '\'' || beforeStart == '"') && (afterEnd == '\'' || afterEnd == '"')) { + + // Skip preceding and following quote + start -= 1; + end += 1; + } + } + + result.replace(start, end, valueForBinding); } return result.toString(); @@ -137,16 +135,17 @@ class ExpressionEvaluatingParameterBinder { /** * Returns the serialized value to be used for the given {@link ParameterBinding}. * - * @param accessor + * @param accessor must not be {@literal null}. * @param parameters - * @param binding + * @param binding must not be {@literal null}. * @return */ private String getParameterValueForBinding(MongoParameterAccessor accessor, MongoParameters parameters, ParameterBinding binding) { - Object value = binding.isExpression() ? evaluateExpression(binding.getExpression(), parameters, - accessor.getValues()) : accessor.getBindableValue(binding.getParameterIndex()); + Object value = binding.isExpression() + ? evaluateExpression(binding.getExpression(), parameters, accessor.getValues()) + : accessor.getBindableValue(binding.getParameterIndex()); if (value instanceof String && binding.isQuoted()) { return (String) value; @@ -155,9 +154,11 @@ class ExpressionEvaluatingParameterBinder { if (value instanceof byte[]) { String base64representation = DatatypeConverter.printBase64Binary((byte[]) value); + if (!binding.isQuoted()) { return "{ '$binary' : '" + base64representation + "', '$type' : " + BSON.B_GENERAL + "}"; } + return base64representation; } @@ -167,9 +168,9 @@ class ExpressionEvaluatingParameterBinder { /** * Evaluates the given {@code expressionString}. * - * @param expressionString - * @param parameters - * @param parameterValues + * @param expressionString must not be {@literal null} or empty. + * @param parameters must not be {@literal null}. + * @param parameterValues must not be {@literal null}. * @return */ private Object evaluateExpression(String expressionString, MongoParameters parameters, Object[] parameterValues) {