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:
Mark Paluch
2017-01-02 10:18:01 +01:00
parent 6f278ce838
commit b9aa410ac1
3 changed files with 71 additions and 39 deletions

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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);
}
----