DATAMONGO-1575 - Polishing.
Extend year range in license headers. Use MongoDB JSON serializer for String escaping. Move unquoting/quote checking to inner QuotedString utility class. Reformat code.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-2017 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.
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -39,10 +39,11 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
* {@link ExpressionEvaluatingParameterBinder} allows to evaluate, convert and bind parameters to placholders within a
|
||||
* {@link ExpressionEvaluatingParameterBinder} allows to evaluate, convert and bind parameters to placeholders within a
|
||||
* {@link String}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
@@ -143,6 +144,8 @@ class ExpressionEvaluatingParameterBinder {
|
||||
*
|
||||
* @param buffer the {@link StringBuffer} to operate upon.
|
||||
* @param valueForBinding the actual binding value.
|
||||
* @param raw the raw binding value
|
||||
* @param isExpression {@literal true} if the binding value results from a SpEL expression.
|
||||
*/
|
||||
private void postProcessQuotedBinding(StringBuffer buffer, String valueForBinding, Object raw, boolean isExpression) {
|
||||
|
||||
@@ -160,8 +163,8 @@ class ExpressionEvaluatingParameterBinder {
|
||||
quotationMark = buffer.charAt(quotationMarkIndex);
|
||||
}
|
||||
|
||||
if (valueForBinding.startsWith("{") && (raw instanceof DBObject || isExpression)) { // remove quotation char before
|
||||
// the complex object string
|
||||
// remove quotation char before the complex object string
|
||||
if (valueForBinding.startsWith("{") && (raw instanceof DBObject || isExpression)) {
|
||||
|
||||
buffer.deleteCharAt(quotationMarkIndex);
|
||||
|
||||
@@ -196,7 +199,7 @@ class ExpressionEvaluatingParameterBinder {
|
||||
return (String) value;
|
||||
}
|
||||
|
||||
return ((String) value).replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
return QuotedString.unquote(JSON.serialize(value));
|
||||
}
|
||||
|
||||
if (value instanceof byte[]) {
|
||||
@@ -254,31 +257,32 @@ class ExpressionEvaluatingParameterBinder {
|
||||
* Extract the placeholder stripping any trailing trailing quotation mark that might have resulted from the
|
||||
* {@link #createReplacementPattern(List) pattern} used.
|
||||
*
|
||||
* @param matcher The actual {@link Matcher#group() group}.
|
||||
* @param parameterIndex The actual parameter index.
|
||||
* @param matcher The actual {@link Matcher}.
|
||||
* @return
|
||||
*/
|
||||
private Placeholder extractPlaceholder(int parameterIndex, Matcher matcher) {
|
||||
|
||||
if (matcher.groupCount() > 1) {
|
||||
|
||||
String suffix = matcher.group(parameterIndex * 2 + 2);
|
||||
String rawPlaceholder = matcher.group(parameterIndex * 2 + 1);
|
||||
String suffix = matcher.group(parameterIndex * 2 + 2);
|
||||
|
||||
if (!StringUtils.hasText(rawPlaceholder)) {
|
||||
|
||||
rawPlaceholder = matcher.group();
|
||||
suffix = ""+rawPlaceholder.charAt(rawPlaceholder.length()-1);
|
||||
if(rawPlaceholder.endsWith("'")) {
|
||||
rawPlaceholder = rawPlaceholder.substring(0, rawPlaceholder.length()-1);
|
||||
suffix = "" + rawPlaceholder.charAt(rawPlaceholder.length() - 1);
|
||||
if (QuotedString.endsWithQuote(rawPlaceholder)) {
|
||||
rawPlaceholder = QuotedString.unquoteSuffix(rawPlaceholder);
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(suffix)) {
|
||||
|
||||
boolean quoted= (suffix.endsWith("'") || suffix.endsWith("\""));
|
||||
boolean quoted = QuotedString.endsWithQuote(suffix);
|
||||
|
||||
return Placeholder.of(parameterIndex, rawPlaceholder, quoted,
|
||||
quoted ? suffix.substring(0, suffix.length() - 1) : suffix);
|
||||
quoted ? QuotedString.unquoteSuffix(suffix) : suffix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,4 +392,41 @@ class ExpressionEvaluatingParameterBinder {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to handle quoted strings using single/double quotes.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@UtilityClass
|
||||
static class QuotedString {
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return {@literal true} if {@literal string} ends with a single/double quote.
|
||||
*/
|
||||
static boolean endsWithQuote(String string) {
|
||||
return string.endsWith("'") || string.endsWith("\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove trailing quoting from {@literal quoted}.
|
||||
*
|
||||
* @param quoted
|
||||
* @return {@literal quoted} with removed quotes.
|
||||
*/
|
||||
public static String unquoteSuffix(String quoted) {
|
||||
return quoted.substring(0, quoted.length() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove leading and trailing quoting from {@literal quoted}.
|
||||
*
|
||||
* @param quoted
|
||||
* @return {@literal quoted} with removed quotes.
|
||||
*/
|
||||
public static String unquote(String quoted) {
|
||||
return quoted.substring(1, quoted.length() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,9 +363,9 @@ public class StringBasedMongoQueryUnitTests {
|
||||
public void shouldQuoteStringReplacementCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews', password: 'foo");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews', password: 'foo");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is(not(new BasicDBObjectBuilder().add("lastname", "Matthews").add("password", "foo").get())));
|
||||
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "Matthews', password: 'foo")));
|
||||
@@ -375,9 +375,9 @@ public class StringBasedMongoQueryUnitTests {
|
||||
public void shouldQuoteStringReplacementContainingQuotesCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews\", password: \"foo");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Matthews\", password: \"foo");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is(not(new BasicDBObjectBuilder().add("lastname", "Matthews").add("password", "foo").get())));
|
||||
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "Matthews\", password: \"foo")));
|
||||
@@ -387,54 +387,47 @@ public class StringBasedMongoQueryUnitTests {
|
||||
public void shouldQuoteStringReplacementWithQuotationsCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter,
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
|
||||
"\"Dave Matthews\", password: 'foo");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is((DBObject) new BasicDBObject("lastname", "\"Dave Matthews\", password: 'foo")));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1565, DATAMONGO-1575
|
||||
public void shouldQuoteComplexQueryStringCorreclty() throws Exception {
|
||||
public void shouldQuoteComplexQueryStringCorrectly() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "{ $ne : \"calamity\" }");
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "{ $ne : \"calamity\" }");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is((DBObject) new BasicDBObject("lastname", "{ $ne : \"calamity\" }")));
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "{ $ne : \"calamity\" }")));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1565, DATAMONGO-1575
|
||||
public void shouldQuotationInQuotedComplexQueryString() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter,
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
|
||||
"{ $ne : \"\\\"calamity\\\"\" }");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
assertThat(query.getQueryObject(),
|
||||
is((DBObject) new BasicDBObject("lastname", "{ $ne : \"\\\"calamity\\\"\" }")));
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("lastname", "{ $ne : \"\\\"calamity\\\"\" }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1575
|
||||
*/
|
||||
@Test
|
||||
@Test // DATAMONGO-1575
|
||||
public void shouldTakeBsonParameterAsIs() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByWithBsonArgument", DBObject.class);
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, new BasicDBObject("$regex", "^calamity$"));
|
||||
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
|
||||
new BasicDBObject("$regex", "^calamity$"));
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
assertThat(query.getQueryObject(), is((DBObject) new BasicDBObject("arg0", Pattern.compile("^calamity$"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1575
|
||||
*/
|
||||
@Test
|
||||
@Test // DATAMONGO-1575
|
||||
public void shouldReplaceParametersInInQuotedExpressionOfNestedQueryOperator() throws Exception {
|
||||
|
||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameRegex", String.class);
|
||||
|
||||
@@ -399,7 +399,6 @@ public interface PersonRepository extends MongoRepository<Person, String>
|
||||
|
||||
@Query("{'lastname': ?#{[0]} }")
|
||||
List<Person> findByQueryWithExpression(String param0);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -412,7 +411,6 @@ public interface PersonRepository extends MongoRepository<Person, String>
|
||||
|
||||
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
|
||||
List<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
Reference in New Issue
Block a user