DATACMNS-867 - Preserve underscores when resolving PropertyPath for PersistentProperties.

Preserve underscores in variable names (eg. String foo_bar) when resolving the actual type information for the property via PropertyPath by quoting (Pattern#quote) the property name.
This commit is contained in:
Christoph Strobl
2017-01-27 10:14:43 +01:00
committed by Oliver Gierke
parent 2da5acc553
commit 47e2e61ff5
4 changed files with 39 additions and 8 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2014 the original author or authors.
* Copyright 2011-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.
@@ -36,6 +36,7 @@ import org.springframework.util.StringUtils;
* Abstraction of a {@link PropertyPath} of a domain class.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
@EqualsAndHashCode
public class PropertyPath implements Streamable<PropertyPath> {
@@ -43,6 +44,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
private static final String DELIMITERS = "_\\.";
private static final String ALL_UPPERCASE = "[A-Z0-9._$]+";
private static final Pattern SPLITTER = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", DELIMITERS));
private static final Pattern SPLITTER_FOR_QUOTED = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", "\\."));
private final TypeInformation<?> owningType;
private final String name;
@@ -59,7 +61,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
* @param owningType must not be {@literal null}.
*/
PropertyPath(String name, Class<?> owningType) {
this(name, ClassTypeInformation.from(owningType), Collections.<PropertyPath>emptyList());
this(name, ClassTypeInformation.from(owningType), Collections.<PropertyPath> emptyList());
}
/**
@@ -210,8 +212,9 @@ public class PropertyPath implements Streamable<PropertyPath> {
}
/**
* Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}.
*
* Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}. <br />
* Uses {@link #SPLITTER} by default and {@link #SPLITTER_FOR_QUOTED} for {@link Pattern#quote(String) quoted} literals.
*
* @param source must not be {@literal null}.
* @param type
* @return
@@ -222,7 +225,9 @@ public class PropertyPath implements Streamable<PropertyPath> {
Assert.notNull(type, "TypeInformation must not be null or empty!");
List<String> iteratorSource = new ArrayList<>();
Matcher matcher = SPLITTER.matcher("_" + source);
Matcher matcher = isQuoted(source) ? SPLITTER_FOR_QUOTED.matcher(source.replace("\\Q", "").replace("\\E", ""))
: SPLITTER.matcher("_" + source);
while (matcher.find()) {
iteratorSource.add(matcher.group(1));
@@ -245,6 +250,10 @@ public class PropertyPath implements Streamable<PropertyPath> {
return result;
}
private static boolean isQuoted(String source) {
return source.matches("^\\\\Q.*\\\\E$");
}
/**
* Creates a new {@link PropertyPath} as subordinary of the given {@link PropertyPath}.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2015 the original author or authors.
* Copyright 2011-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.
@@ -23,6 +23,7 @@ import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import org.springframework.data.annotation.Reference;
import org.springframework.data.mapping.Association;
@@ -66,8 +67,7 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
this.name = property.getName();
this.rawType = property.getType();
this.information = PropertyPath.from(property.getName(), owner.getTypeInformation()).getTypeInformation();
this.information = PropertyPath.from(Pattern.quote(property.getName()), owner.getTypeInformation()).getTypeInformation();
this.property = property;
this.association = isAssociation() ? Optional.of(createAssociation()) : Optional.empty();
this.owner = owner;

View File

@@ -21,6 +21,7 @@ import static org.springframework.data.mapping.PropertyPath.*;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.junit.Rule;
import org.junit.Test;
@@ -32,6 +33,7 @@ import org.springframework.data.util.TypeInformation;
* Unit tests for {@link PropertyPath}.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
@SuppressWarnings("unused")
public class PropertyPathUnitTests {
@@ -333,11 +335,22 @@ public class PropertyPathUnitTests {
from("userAme", Foo.class);
}
@Test // DATACMNS-867
public void preservesUnderscoresForQuotedNames() {
PropertyPath path = from(Pattern.quote("var_name_with_underscore"), Foo.class);
assertThat(path).isNotNull();
assertThat(path.getSegment()).isEqualTo("var_name_with_underscore");
assertThat(path.hasNext()).isFalse();
}
private class Foo {
String userName;
String _email;
String UUID;
String var_name_with_underscore;
}
private class Bar {

View File

@@ -43,6 +43,7 @@ import org.springframework.util.ReflectionUtils;
* Unit tests for {@link AbstractPersistentProperty}.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class AbstractPersistentPropertyUnitTests {
@@ -192,6 +193,13 @@ public class AbstractPersistentPropertyUnitTests {
assertThat(property.isEntity()).isFalse();
}
@Test // DATACMNS-867
public void resolvesFieldNameWithUnderscoresCorrectly() {
SamplePersistentProperty property = getProperty(TestClassComplex.class, "var_name_with_underscores");
assertThat(property.getName()).isEqualTo("var_name_with_underscores");
}
private <T> BasicPersistentEntity<T, SamplePersistentProperty> getEntity(Class<T> type) {
return new BasicPersistentEntity<>(ClassTypeInformation.from(type));
}
@@ -244,6 +252,7 @@ public class AbstractPersistentPropertyUnitTests {
Map map;
Collection collection;
transient Object transientField;
String var_name_with_underscores;
}
class AccessorTestClass {