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");
|
* 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.
|
||||||
@@ -15,9 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.mongodb.repository.query;
|
package org.springframework.data.mongodb.repository.query;
|
||||||
|
|
||||||
import com.mongodb.DBObject;
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -39,10 +39,11 @@ import org.springframework.util.Assert;
|
|||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import com.mongodb.DBObject;
|
||||||
import com.mongodb.util.JSON;
|
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}.
|
* {@link String}.
|
||||||
*
|
*
|
||||||
* @author Christoph Strobl
|
* @author Christoph Strobl
|
||||||
@@ -143,6 +144,8 @@ class ExpressionEvaluatingParameterBinder {
|
|||||||
*
|
*
|
||||||
* @param buffer the {@link StringBuffer} to operate upon.
|
* @param buffer the {@link StringBuffer} to operate upon.
|
||||||
* @param valueForBinding the actual binding value.
|
* @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) {
|
private void postProcessQuotedBinding(StringBuffer buffer, String valueForBinding, Object raw, boolean isExpression) {
|
||||||
|
|
||||||
@@ -160,8 +163,8 @@ class ExpressionEvaluatingParameterBinder {
|
|||||||
quotationMark = buffer.charAt(quotationMarkIndex);
|
quotationMark = buffer.charAt(quotationMarkIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valueForBinding.startsWith("{") && (raw instanceof DBObject || isExpression)) { // remove quotation char before
|
// remove quotation char before the complex object string
|
||||||
// the complex object string
|
if (valueForBinding.startsWith("{") && (raw instanceof DBObject || isExpression)) {
|
||||||
|
|
||||||
buffer.deleteCharAt(quotationMarkIndex);
|
buffer.deleteCharAt(quotationMarkIndex);
|
||||||
|
|
||||||
@@ -196,7 +199,7 @@ class ExpressionEvaluatingParameterBinder {
|
|||||||
return (String) value;
|
return (String) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((String) value).replace("\\", "\\\\").replace("\"", "\\\"");
|
return QuotedString.unquote(JSON.serialize(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof byte[]) {
|
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
|
* Extract the placeholder stripping any trailing trailing quotation mark that might have resulted from the
|
||||||
* {@link #createReplacementPattern(List) pattern} used.
|
* {@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
|
* @return
|
||||||
*/
|
*/
|
||||||
private Placeholder extractPlaceholder(int parameterIndex, Matcher matcher) {
|
private Placeholder extractPlaceholder(int parameterIndex, Matcher matcher) {
|
||||||
|
|
||||||
if (matcher.groupCount() > 1) {
|
if (matcher.groupCount() > 1) {
|
||||||
|
|
||||||
String suffix = matcher.group(parameterIndex * 2 + 2);
|
|
||||||
String rawPlaceholder = matcher.group(parameterIndex * 2 + 1);
|
String rawPlaceholder = matcher.group(parameterIndex * 2 + 1);
|
||||||
|
String suffix = matcher.group(parameterIndex * 2 + 2);
|
||||||
|
|
||||||
if (!StringUtils.hasText(rawPlaceholder)) {
|
if (!StringUtils.hasText(rawPlaceholder)) {
|
||||||
|
|
||||||
rawPlaceholder = matcher.group();
|
rawPlaceholder = matcher.group();
|
||||||
suffix = ""+rawPlaceholder.charAt(rawPlaceholder.length()-1);
|
suffix = "" + rawPlaceholder.charAt(rawPlaceholder.length() - 1);
|
||||||
if(rawPlaceholder.endsWith("'")) {
|
if (QuotedString.endsWithQuote(rawPlaceholder)) {
|
||||||
rawPlaceholder = rawPlaceholder.substring(0, rawPlaceholder.length()-1);
|
rawPlaceholder = QuotedString.unquoteSuffix(rawPlaceholder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.hasText(suffix)) {
|
if (StringUtils.hasText(suffix)) {
|
||||||
|
|
||||||
boolean quoted= (suffix.endsWith("'") || suffix.endsWith("\""));
|
boolean quoted = QuotedString.endsWithQuote(suffix);
|
||||||
|
|
||||||
return Placeholder.of(parameterIndex, rawPlaceholder, quoted,
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -361,9 +361,9 @@ public class StringBasedMongoQueryUnitTests {
|
|||||||
public void shouldQuoteStringReplacementCorrectly() throws Exception {
|
public void shouldQuoteStringReplacementCorrectly() throws Exception {
|
||||||
|
|
||||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
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(),
|
assertThat(query.getQueryObject(),
|
||||||
is(not(new Document().append("lastname", "Matthews").append("password", "foo"))));
|
is(not(new Document().append("lastname", "Matthews").append("password", "foo"))));
|
||||||
assertThat(query.getQueryObject(), is(new Document("lastname", "Matthews', password: 'foo")));
|
assertThat(query.getQueryObject(), is(new Document("lastname", "Matthews', password: 'foo")));
|
||||||
@@ -373,9 +373,9 @@ public class StringBasedMongoQueryUnitTests {
|
|||||||
public void shouldQuoteStringReplacementContainingQuotesCorrectly() throws Exception {
|
public void shouldQuoteStringReplacementContainingQuotesCorrectly() throws Exception {
|
||||||
|
|
||||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
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(),
|
assertThat(query.getQueryObject(),
|
||||||
is(not(new Document().append("lastname", "Matthews").append("password", "foo"))));
|
is(not(new Document().append("lastname", "Matthews").append("password", "foo"))));
|
||||||
assertThat(query.getQueryObject(), is(new Document("lastname", "Matthews\", password: \"foo")));
|
assertThat(query.getQueryObject(), is(new Document("lastname", "Matthews\", password: \"foo")));
|
||||||
@@ -385,20 +385,20 @@ public class StringBasedMongoQueryUnitTests {
|
|||||||
public void shouldQuoteStringReplacementWithQuotationsCorrectly() throws Exception {
|
public void shouldQuoteStringReplacementWithQuotationsCorrectly() throws Exception {
|
||||||
|
|
||||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter,
|
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
|
||||||
"\"Dave Matthews\", password: 'foo");
|
"\"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(new Document("lastname", "\"Dave Matthews\", password: 'foo")));
|
assertThat(query.getQueryObject(), is(new Document("lastname", "\"Dave Matthews\", password: 'foo")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // DATAMONGO-1565, DATAMONGO-1575
|
@Test // DATAMONGO-1565, DATAMONGO-1575
|
||||||
public void shouldQuoteComplexQueryStringCorreclty() throws Exception {
|
public void shouldQuoteComplexQueryStringCorrectly() throws Exception {
|
||||||
|
|
||||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
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);
|
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||||
assertThat(query.getQueryObject(), is(new Document("lastname", "{ $ne : \"calamity\" }")));
|
assertThat(query.getQueryObject(), is(new Document("lastname", "{ $ne : \"calamity\" }")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,18 +406,15 @@ public class StringBasedMongoQueryUnitTests {
|
|||||||
public void shouldQuotationInQuotedComplexQueryString() throws Exception {
|
public void shouldQuotationInQuotedComplexQueryString() throws Exception {
|
||||||
|
|
||||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameQuoted", String.class);
|
||||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter,
|
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter,
|
||||||
"{ $ne : \"\\\"calamity\\\"\" }");
|
"{ $ne : \"\\\"calamity\\\"\" }");
|
||||||
|
|
||||||
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(new Document("lastname", "{ $ne : \"\\\"calamity\\\"\" }")));
|
assertThat(query.getQueryObject(), is(new Document("lastname", "{ $ne : \"\\\"calamity\\\"\" }")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Test // DATAMONGO-1575
|
||||||
* @see DATAMONGO-1575
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void shouldTakeBsonParameterAsIs() throws Exception {
|
public void shouldTakeBsonParameterAsIs() throws Exception {
|
||||||
|
|
||||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByWithBsonArgument", Document.class);
|
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByWithBsonArgument", Document.class);
|
||||||
@@ -428,10 +425,7 @@ public class StringBasedMongoQueryUnitTests {
|
|||||||
assertThat(query.getQueryObject(), is(new Document("arg0", new BsonRegularExpression("^calamity$"))));
|
assertThat(query.getQueryObject(), is(new Document("arg0", new BsonRegularExpression("^calamity$"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Test // DATAMONGO-1575
|
||||||
* @see DATAMONGO-1575
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void shouldReplaceParametersInInQuotedExpressionOfNestedQueryOperator() throws Exception {
|
public void shouldReplaceParametersInInQuotedExpressionOfNestedQueryOperator() throws Exception {
|
||||||
|
|
||||||
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameRegex", String.class);
|
StringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameRegex", String.class);
|
||||||
|
|||||||
@@ -401,7 +401,6 @@ public interface PersonRepository extends MongoRepository<Person, String>
|
|||||||
|
|
||||||
@Query("{'lastname': ?#{[0]} }")
|
@Query("{'lastname': ?#{[0]} }")
|
||||||
List<Person> findByQueryWithExpression(String param0);
|
List<Person> findByQueryWithExpression(String param0);
|
||||||
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
@@ -414,7 +413,6 @@ public interface PersonRepository extends MongoRepository<Person, String>
|
|||||||
|
|
||||||
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
|
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
|
||||||
List<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);
|
List<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);
|
||||||
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user